@codemirror/view 6.9.4 → 6.9.6

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,27 @@
1
+ ## 6.9.6 (2023-04-21)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue where, when escape was pressed followed by a key that the editor handled, followed by tab, the tab would still move focus.
6
+
7
+ Fix an issue where, in some circumstances, the editor would ignore text changes at the end of a composition.
8
+
9
+ Allow inline widgets to be updated to a different length via `updateDOM`.
10
+
11
+ ## 6.9.5 (2023-04-17)
12
+
13
+ ### Bug fixes
14
+
15
+ Avoid disrupting the composition in specific cases where Safari invasively changes the DOM structure in the middle of a composition.
16
+
17
+ Fix a bug that prevented `destroy` being called on hover tooltips.
18
+
19
+ Fix a bug where the editor could take focus when content changes required it to restore the DOM selection.
20
+
21
+ Fix height layout corruption caused by a division by zero.
22
+
23
+ Make sure styles targeting the editor's focus status are specific enough to not cause them to apply to editors nested inside another focused editor. This will require themes to adjust their selection background styles to match the new specificity.
24
+
1
25
  ## 6.9.4 (2023-04-11)
2
26
 
3
27
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -856,15 +856,15 @@ class WidgetView extends ContentView {
856
856
  return true;
857
857
  }
858
858
  become(other) {
859
- if (other.length == this.length && other instanceof WidgetView && other.side == this.side) {
860
- if (this.widget.constructor == other.widget.constructor) {
861
- if (!this.widget.compare(other.widget))
862
- this.markDirty(true);
863
- if (this.dom && !this.prevWidget)
864
- this.prevWidget = this.widget;
865
- this.widget = other.widget;
866
- return true;
867
- }
859
+ if (other instanceof WidgetView && other.side == this.side &&
860
+ this.widget.constructor == other.widget.constructor) {
861
+ if (!this.widget.compare(other.widget))
862
+ this.markDirty(true);
863
+ if (this.dom && !this.prevWidget)
864
+ this.prevWidget = this.widget;
865
+ this.widget = other.widget;
866
+ this.length = other.length;
867
+ return true;
868
868
  }
869
869
  return false;
870
870
  }
@@ -910,14 +910,14 @@ class CompositionView extends WidgetView {
910
910
  let { topView, text } = this.widget;
911
911
  if (!topView)
912
912
  return new DOMPos(text, Math.min(pos, text.nodeValue.length));
913
- return scanCompositionTree(pos, 0, topView, text, (v, p) => v.domAtPos(p), p => new DOMPos(text, Math.min(p, text.nodeValue.length)));
913
+ 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)));
914
914
  }
915
915
  sync() { this.setDOM(this.widget.toDOM()); }
916
916
  localPosFromDOM(node, offset) {
917
917
  let { topView, text } = this.widget;
918
918
  if (!topView)
919
919
  return Math.min(offset, this.length);
920
- return posFromDOMInCompositionTree(node, offset, topView, text);
920
+ return posFromDOMInCompositionTree(node, offset, topView, text, this.length - topView.length);
921
921
  }
922
922
  ignoreMutation() { return false; }
923
923
  get overrideDOMText() { return null; }
@@ -925,7 +925,7 @@ class CompositionView extends WidgetView {
925
925
  let { topView, text } = this.widget;
926
926
  if (!topView)
927
927
  return textCoords(text, pos, side);
928
- return scanCompositionTree(pos, side, topView, text, (v, pos, side) => v.coordsAt(pos, side), (pos, side) => textCoords(text, pos, side));
928
+ return scanCompositionTree(pos, side, topView, text, this.length - topView.length, (v, pos, side) => v.coordsAt(pos, side), (text, pos, side) => textCoords(text, pos, side));
929
929
  }
930
930
  destroy() {
931
931
  var _a;
@@ -938,35 +938,68 @@ class CompositionView extends WidgetView {
938
938
  // Uses the old structure of a chunk of content view frozen for
939
939
  // composition to try and find a reasonable DOM location for the given
940
940
  // offset.
941
- function scanCompositionTree(pos, side, view, text, enterView, fromText) {
941
+ function scanCompositionTree(pos, side, view, text, dLen, enterView, fromText) {
942
942
  if (view instanceof MarkView) {
943
943
  for (let child = view.dom.firstChild; child; child = child.nextSibling) {
944
944
  let desc = ContentView.get(child);
945
- if (!desc)
946
- return fromText(pos, side);
947
- let hasComp = contains(child, text);
948
- let len = desc.length + (hasComp ? text.nodeValue.length : 0);
949
- if (pos < len || pos == len && desc.getSide() <= 0)
950
- return hasComp ? scanCompositionTree(pos, side, desc, text, enterView, fromText) : enterView(desc, pos, side);
951
- pos -= len;
945
+ if (!desc) {
946
+ let inner = scanCompositionNode(pos, side, child, fromText);
947
+ if (typeof inner != "number")
948
+ return inner;
949
+ pos = inner;
950
+ }
951
+ else {
952
+ let hasComp = contains(child, text);
953
+ let len = desc.length + (hasComp ? dLen : 0);
954
+ if (pos < len || pos == len && desc.getSide() <= 0)
955
+ return hasComp ? scanCompositionTree(pos, side, desc, text, dLen, enterView, fromText) : enterView(desc, pos, side);
956
+ pos -= len;
957
+ }
952
958
  }
953
959
  return enterView(view, view.length, -1);
954
960
  }
955
961
  else if (view.dom == text) {
956
- return fromText(pos, side);
962
+ return fromText(text, pos, side);
957
963
  }
958
964
  else {
959
965
  return enterView(view, pos, side);
960
966
  }
961
967
  }
962
- function posFromDOMInCompositionTree(node, offset, view, text) {
968
+ function scanCompositionNode(pos, side, node, fromText) {
969
+ if (node.nodeType == 3) {
970
+ let len = node.nodeValue.length;
971
+ if (pos <= len)
972
+ return fromText(node, pos, side);
973
+ pos -= len;
974
+ }
975
+ else if (node.nodeType == 1 && node.contentEditable != "false") {
976
+ for (let child = node.firstChild; child; child = child.nextSibling) {
977
+ let inner = scanCompositionNode(pos, side, child, fromText);
978
+ if (typeof inner != "number")
979
+ return inner;
980
+ pos = inner;
981
+ }
982
+ }
983
+ return pos;
984
+ }
985
+ function posFromDOMInCompositionTree(node, offset, view, text, dLen) {
963
986
  if (view instanceof MarkView) {
964
987
  let pos = 0;
965
- for (let child of view.children) {
966
- let hasComp = contains(child.dom, text);
967
- if (contains(child.dom, node))
968
- return pos + (hasComp ? posFromDOMInCompositionTree(node, offset, child, text) : child.localPosFromDOM(node, offset));
969
- pos += hasComp ? text.nodeValue.length : child.length;
988
+ for (let child = view.dom.firstChild; child; child = child.nextSibling) {
989
+ let childView = ContentView.get(child);
990
+ if (childView) {
991
+ let hasComp = contains(child, text);
992
+ if (contains(child, node))
993
+ return pos + (hasComp ? posFromDOMInCompositionTree(node, offset, childView, text, dLen)
994
+ : childView.localPosFromDOM(node, offset));
995
+ pos += childView.length + (hasComp ? dLen : 0);
996
+ }
997
+ else {
998
+ let inner = posFromDOMInOpaqueNode(node, offset, child);
999
+ if (inner.result != null)
1000
+ return pos + inner.result;
1001
+ pos += inner.size;
1002
+ }
970
1003
  }
971
1004
  }
972
1005
  else if (view.dom == text) {
@@ -974,6 +1007,27 @@ function posFromDOMInCompositionTree(node, offset, view, text) {
974
1007
  }
975
1008
  return view.localPosFromDOM(node, offset);
976
1009
  }
1010
+ function posFromDOMInOpaqueNode(node, offset, target) {
1011
+ if (target.nodeType == 3) {
1012
+ return node == target ? { result: offset } : { size: target.nodeValue.length };
1013
+ }
1014
+ else if (target.nodeType == 1 && target.contentEditable != "false") {
1015
+ let pos = 0;
1016
+ for (let child = target.firstChild, i = 0;; child = child.nextSibling, i++) {
1017
+ if (node == target && i == offset)
1018
+ return { result: pos };
1019
+ if (!child)
1020
+ return { size: pos };
1021
+ let inner = posFromDOMInOpaqueNode(node, offset, child);
1022
+ if (inner.result != null)
1023
+ return { result: offset + inner.result };
1024
+ pos += inner.size;
1025
+ }
1026
+ }
1027
+ else {
1028
+ return target.contains(node) ? { result: 0 } : { size: 0 };
1029
+ }
1030
+ }
977
1031
  // These are drawn around uneditable widgets to avoid a number of
978
1032
  // browser bugs that show up when the cursor is directly next to
979
1033
  // uneditable inline content.
@@ -2648,7 +2702,10 @@ class DocView extends ContentView {
2648
2702
  updateSelection(mustRead = false, fromPointer = false) {
2649
2703
  if (mustRead || !this.view.observer.selectionRange.focusNode)
2650
2704
  this.view.observer.readSelectionRange();
2651
- if (!(fromPointer || this.mayControlSelection()))
2705
+ let activeElt = this.view.root.activeElement, focused = activeElt == this.dom;
2706
+ let selectionNotFocus = !focused &&
2707
+ hasSelection(this.dom, this.view.observer.selectionRange) && !(activeElt && this.dom.contains(activeElt));
2708
+ if (!(focused || fromPointer || selectionNotFocus))
2652
2709
  return;
2653
2710
  let force = this.forceSelection;
2654
2711
  this.forceSelection = false;
@@ -2718,6 +2775,11 @@ class DocView extends ContentView {
2718
2775
  rawSel.removeAllRanges();
2719
2776
  rawSel.addRange(range);
2720
2777
  }
2778
+ if (selectionNotFocus && this.view.root.activeElement == this.dom) {
2779
+ this.dom.blur();
2780
+ if (activeElt)
2781
+ activeElt.focus();
2782
+ }
2721
2783
  });
2722
2784
  this.view.observer.setSelectionRange(anchor, head);
2723
2785
  }
@@ -2751,11 +2813,6 @@ class DocView extends ContentView {
2751
2813
  if (view.docView.posFromDOM(newRange.anchorNode, newRange.anchorOffset) != cursor.from)
2752
2814
  sel.collapse(anchorNode, anchorOffset);
2753
2815
  }
2754
- mayControlSelection() {
2755
- let active = this.view.root.activeElement;
2756
- return active == this.dom ||
2757
- hasSelection(this.dom, this.view.observer.selectionRange) && !(active && this.dom.contains(active));
2758
- }
2759
2816
  nearest(dom) {
2760
2817
  for (let cur = dom; cur;) {
2761
2818
  let domView = ContentView.get(cur);
@@ -3574,6 +3631,8 @@ class InputState {
3574
3631
  this.lastKeyTime = Date.now();
3575
3632
  if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
3576
3633
  return true;
3634
+ if (event.keyCode != 27 && modifierCodes.indexOf(event.keyCode) < 0)
3635
+ view.inputState.lastEscPress = 0;
3577
3636
  // Chrome for Android usually doesn't fire proper key events, but
3578
3637
  // occasionally does, usually surrounded by a bunch of complicated
3579
3638
  // composition changes. When an enter or backspace key event is
@@ -3843,8 +3902,6 @@ handlers.keydown = (view, event) => {
3843
3902
  view.inputState.setSelectionOrigin("select");
3844
3903
  if (event.keyCode == 27)
3845
3904
  view.inputState.lastEscPress = Date.now();
3846
- else if (modifierCodes.indexOf(event.keyCode) < 0)
3847
- view.inputState.lastEscPress = 0;
3848
3905
  };
3849
3906
  handlers.touchstart = (view, e) => {
3850
3907
  view.inputState.lastTouchTime = Date.now();
@@ -4146,13 +4203,23 @@ handlers.compositionend = view => {
4146
4203
  view.inputState.compositionPendingKey = true;
4147
4204
  view.inputState.compositionPendingChange = view.observer.pendingRecords().length > 0;
4148
4205
  view.inputState.compositionFirstChange = null;
4149
- if (browser.chrome && browser.android)
4206
+ if (browser.chrome && browser.android) {
4207
+ // Delay flushing for a bit on Android because it'll often fire a
4208
+ // bunch of contradictory changes in a row at end of compositon
4150
4209
  view.observer.flushSoon();
4151
- setTimeout(() => {
4152
- // Force the composition state to be cleared if it hasn't already been
4153
- if (view.inputState.composing < 0 && view.docView.compositionDeco.size)
4154
- view.update([]);
4155
- }, 50);
4210
+ }
4211
+ else if (view.inputState.compositionPendingChange) {
4212
+ // If we found pending records, schedule a flush.
4213
+ Promise.resolve().then(() => view.observer.flush());
4214
+ }
4215
+ else {
4216
+ // Otherwise, make sure that, if no changes come in soon, the
4217
+ // composition view is cleared.
4218
+ setTimeout(() => {
4219
+ if (view.inputState.composing < 0 && view.docView.compositionDeco.size)
4220
+ view.update([]);
4221
+ }, 50);
4222
+ }
4156
4223
  };
4157
4224
  handlers.contextmenu = view => {
4158
4225
  view.inputState.lastContextMenu = Date.now();
@@ -4492,7 +4559,8 @@ class HeightMapGap extends HeightMap {
4492
4559
  if (oracle.lineWrapping) {
4493
4560
  let totalPerLine = Math.min(this.height, oracle.lineHeight * lines);
4494
4561
  perLine = totalPerLine / lines;
4495
- perChar = (this.height - totalPerLine) / (this.length - lines - 1);
4562
+ if (this.length > lines + 1)
4563
+ perChar = (this.height - totalPerLine) / (this.length - lines - 1);
4496
4564
  }
4497
4565
  else {
4498
4566
  perLine = this.height / lines;
@@ -5508,16 +5576,16 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
5508
5576
  "&dark .cm-selectionBackground": {
5509
5577
  background: "#222"
5510
5578
  },
5511
- "&light.cm-focused .cm-selectionBackground": {
5579
+ "&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
5512
5580
  background: "#d7d4f0"
5513
5581
  },
5514
- "&dark.cm-focused .cm-selectionBackground": {
5582
+ "&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
5515
5583
  background: "#233"
5516
5584
  },
5517
5585
  ".cm-cursorLayer": {
5518
5586
  pointerEvents: "none"
5519
5587
  },
5520
- "&.cm-focused .cm-cursorLayer": {
5588
+ "&.cm-focused > .cm-scroller > .cm-cursorLayer": {
5521
5589
  animation: "steps(1) cm-blink 1.2s infinite"
5522
5590
  },
5523
5591
  // Two animations defined so that we can switch between them to
@@ -5539,7 +5607,7 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
5539
5607
  ".cm-dropCursor": {
5540
5608
  position: "absolute"
5541
5609
  },
5542
- "&.cm-focused .cm-cursor": {
5610
+ "&.cm-focused > .cm-scroller > .cm-cursorLayer .cm-cursor": {
5543
5611
  display: "block"
5544
5612
  },
5545
5613
  "&light .cm-activeLine": { backgroundColor: "#cceeff44" },
@@ -8811,6 +8879,11 @@ class HoverTooltipHost {
8811
8879
  update(update) {
8812
8880
  this.manager.update(update);
8813
8881
  }
8882
+ destroy() {
8883
+ var _a;
8884
+ for (let t of this.manager.tooltipViews)
8885
+ (_a = t.destroy) === null || _a === void 0 ? void 0 : _a.call(t);
8886
+ }
8814
8887
  }
8815
8888
  const showHoverTooltipHost = showTooltip.compute([showHoverTooltip], state => {
8816
8889
  let tooltips = state.facet(showHoverTooltip).filter(t => t);
package/dist/index.js CHANGED
@@ -852,15 +852,15 @@ class WidgetView extends ContentView {
852
852
  return true;
853
853
  }
854
854
  become(other) {
855
- if (other.length == this.length && other instanceof WidgetView && other.side == this.side) {
856
- if (this.widget.constructor == other.widget.constructor) {
857
- if (!this.widget.compare(other.widget))
858
- this.markDirty(true);
859
- if (this.dom && !this.prevWidget)
860
- this.prevWidget = this.widget;
861
- this.widget = other.widget;
862
- return true;
863
- }
855
+ if (other instanceof WidgetView && other.side == this.side &&
856
+ this.widget.constructor == other.widget.constructor) {
857
+ if (!this.widget.compare(other.widget))
858
+ this.markDirty(true);
859
+ if (this.dom && !this.prevWidget)
860
+ this.prevWidget = this.widget;
861
+ this.widget = other.widget;
862
+ this.length = other.length;
863
+ return true;
864
864
  }
865
865
  return false;
866
866
  }
@@ -906,14 +906,14 @@ class CompositionView extends WidgetView {
906
906
  let { topView, text } = this.widget;
907
907
  if (!topView)
908
908
  return new DOMPos(text, Math.min(pos, text.nodeValue.length));
909
- return scanCompositionTree(pos, 0, topView, text, (v, p) => v.domAtPos(p), p => new DOMPos(text, Math.min(p, text.nodeValue.length)));
909
+ 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)));
910
910
  }
911
911
  sync() { this.setDOM(this.widget.toDOM()); }
912
912
  localPosFromDOM(node, offset) {
913
913
  let { topView, text } = this.widget;
914
914
  if (!topView)
915
915
  return Math.min(offset, this.length);
916
- return posFromDOMInCompositionTree(node, offset, topView, text);
916
+ return posFromDOMInCompositionTree(node, offset, topView, text, this.length - topView.length);
917
917
  }
918
918
  ignoreMutation() { return false; }
919
919
  get overrideDOMText() { return null; }
@@ -921,7 +921,7 @@ class CompositionView extends WidgetView {
921
921
  let { topView, text } = this.widget;
922
922
  if (!topView)
923
923
  return textCoords(text, pos, side);
924
- return scanCompositionTree(pos, side, topView, text, (v, pos, side) => v.coordsAt(pos, side), (pos, side) => textCoords(text, pos, side));
924
+ return scanCompositionTree(pos, side, topView, text, this.length - topView.length, (v, pos, side) => v.coordsAt(pos, side), (text, pos, side) => textCoords(text, pos, side));
925
925
  }
926
926
  destroy() {
927
927
  var _a;
@@ -934,35 +934,68 @@ class CompositionView extends WidgetView {
934
934
  // Uses the old structure of a chunk of content view frozen for
935
935
  // composition to try and find a reasonable DOM location for the given
936
936
  // offset.
937
- function scanCompositionTree(pos, side, view, text, enterView, fromText) {
937
+ function scanCompositionTree(pos, side, view, text, dLen, enterView, fromText) {
938
938
  if (view instanceof MarkView) {
939
939
  for (let child = view.dom.firstChild; child; child = child.nextSibling) {
940
940
  let desc = ContentView.get(child);
941
- if (!desc)
942
- return fromText(pos, side);
943
- let hasComp = contains(child, text);
944
- let len = desc.length + (hasComp ? text.nodeValue.length : 0);
945
- if (pos < len || pos == len && desc.getSide() <= 0)
946
- return hasComp ? scanCompositionTree(pos, side, desc, text, enterView, fromText) : enterView(desc, pos, side);
947
- pos -= len;
941
+ if (!desc) {
942
+ let inner = scanCompositionNode(pos, side, child, fromText);
943
+ if (typeof inner != "number")
944
+ return inner;
945
+ pos = inner;
946
+ }
947
+ else {
948
+ let hasComp = contains(child, text);
949
+ let len = desc.length + (hasComp ? dLen : 0);
950
+ if (pos < len || pos == len && desc.getSide() <= 0)
951
+ return hasComp ? scanCompositionTree(pos, side, desc, text, dLen, enterView, fromText) : enterView(desc, pos, side);
952
+ pos -= len;
953
+ }
948
954
  }
949
955
  return enterView(view, view.length, -1);
950
956
  }
951
957
  else if (view.dom == text) {
952
- return fromText(pos, side);
958
+ return fromText(text, pos, side);
953
959
  }
954
960
  else {
955
961
  return enterView(view, pos, side);
956
962
  }
957
963
  }
958
- function posFromDOMInCompositionTree(node, offset, view, text) {
964
+ function scanCompositionNode(pos, side, node, fromText) {
965
+ if (node.nodeType == 3) {
966
+ let len = node.nodeValue.length;
967
+ if (pos <= len)
968
+ return fromText(node, pos, side);
969
+ pos -= len;
970
+ }
971
+ else if (node.nodeType == 1 && node.contentEditable != "false") {
972
+ for (let child = node.firstChild; child; child = child.nextSibling) {
973
+ let inner = scanCompositionNode(pos, side, child, fromText);
974
+ if (typeof inner != "number")
975
+ return inner;
976
+ pos = inner;
977
+ }
978
+ }
979
+ return pos;
980
+ }
981
+ function posFromDOMInCompositionTree(node, offset, view, text, dLen) {
959
982
  if (view instanceof MarkView) {
960
983
  let pos = 0;
961
- for (let child of view.children) {
962
- let hasComp = contains(child.dom, text);
963
- if (contains(child.dom, node))
964
- return pos + (hasComp ? posFromDOMInCompositionTree(node, offset, child, text) : child.localPosFromDOM(node, offset));
965
- pos += hasComp ? text.nodeValue.length : child.length;
984
+ for (let child = view.dom.firstChild; child; child = child.nextSibling) {
985
+ let childView = ContentView.get(child);
986
+ if (childView) {
987
+ let hasComp = contains(child, text);
988
+ if (contains(child, node))
989
+ return pos + (hasComp ? posFromDOMInCompositionTree(node, offset, childView, text, dLen)
990
+ : childView.localPosFromDOM(node, offset));
991
+ pos += childView.length + (hasComp ? dLen : 0);
992
+ }
993
+ else {
994
+ let inner = posFromDOMInOpaqueNode(node, offset, child);
995
+ if (inner.result != null)
996
+ return pos + inner.result;
997
+ pos += inner.size;
998
+ }
966
999
  }
967
1000
  }
968
1001
  else if (view.dom == text) {
@@ -970,6 +1003,27 @@ function posFromDOMInCompositionTree(node, offset, view, text) {
970
1003
  }
971
1004
  return view.localPosFromDOM(node, offset);
972
1005
  }
1006
+ function posFromDOMInOpaqueNode(node, offset, target) {
1007
+ if (target.nodeType == 3) {
1008
+ return node == target ? { result: offset } : { size: target.nodeValue.length };
1009
+ }
1010
+ else if (target.nodeType == 1 && target.contentEditable != "false") {
1011
+ let pos = 0;
1012
+ for (let child = target.firstChild, i = 0;; child = child.nextSibling, i++) {
1013
+ if (node == target && i == offset)
1014
+ return { result: pos };
1015
+ if (!child)
1016
+ return { size: pos };
1017
+ let inner = posFromDOMInOpaqueNode(node, offset, child);
1018
+ if (inner.result != null)
1019
+ return { result: offset + inner.result };
1020
+ pos += inner.size;
1021
+ }
1022
+ }
1023
+ else {
1024
+ return target.contains(node) ? { result: 0 } : { size: 0 };
1025
+ }
1026
+ }
973
1027
  // These are drawn around uneditable widgets to avoid a number of
974
1028
  // browser bugs that show up when the cursor is directly next to
975
1029
  // uneditable inline content.
@@ -2642,7 +2696,10 @@ class DocView extends ContentView {
2642
2696
  updateSelection(mustRead = false, fromPointer = false) {
2643
2697
  if (mustRead || !this.view.observer.selectionRange.focusNode)
2644
2698
  this.view.observer.readSelectionRange();
2645
- if (!(fromPointer || this.mayControlSelection()))
2699
+ let activeElt = this.view.root.activeElement, focused = activeElt == this.dom;
2700
+ let selectionNotFocus = !focused &&
2701
+ hasSelection(this.dom, this.view.observer.selectionRange) && !(activeElt && this.dom.contains(activeElt));
2702
+ if (!(focused || fromPointer || selectionNotFocus))
2646
2703
  return;
2647
2704
  let force = this.forceSelection;
2648
2705
  this.forceSelection = false;
@@ -2712,6 +2769,11 @@ class DocView extends ContentView {
2712
2769
  rawSel.removeAllRanges();
2713
2770
  rawSel.addRange(range);
2714
2771
  }
2772
+ if (selectionNotFocus && this.view.root.activeElement == this.dom) {
2773
+ this.dom.blur();
2774
+ if (activeElt)
2775
+ activeElt.focus();
2776
+ }
2715
2777
  });
2716
2778
  this.view.observer.setSelectionRange(anchor, head);
2717
2779
  }
@@ -2745,11 +2807,6 @@ class DocView extends ContentView {
2745
2807
  if (view.docView.posFromDOM(newRange.anchorNode, newRange.anchorOffset) != cursor.from)
2746
2808
  sel.collapse(anchorNode, anchorOffset);
2747
2809
  }
2748
- mayControlSelection() {
2749
- let active = this.view.root.activeElement;
2750
- return active == this.dom ||
2751
- hasSelection(this.dom, this.view.observer.selectionRange) && !(active && this.dom.contains(active));
2752
- }
2753
2810
  nearest(dom) {
2754
2811
  for (let cur = dom; cur;) {
2755
2812
  let domView = ContentView.get(cur);
@@ -3568,6 +3625,8 @@ class InputState {
3568
3625
  this.lastKeyTime = Date.now();
3569
3626
  if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
3570
3627
  return true;
3628
+ if (event.keyCode != 27 && modifierCodes.indexOf(event.keyCode) < 0)
3629
+ view.inputState.lastEscPress = 0;
3571
3630
  // Chrome for Android usually doesn't fire proper key events, but
3572
3631
  // occasionally does, usually surrounded by a bunch of complicated
3573
3632
  // composition changes. When an enter or backspace key event is
@@ -3837,8 +3896,6 @@ handlers.keydown = (view, event) => {
3837
3896
  view.inputState.setSelectionOrigin("select");
3838
3897
  if (event.keyCode == 27)
3839
3898
  view.inputState.lastEscPress = Date.now();
3840
- else if (modifierCodes.indexOf(event.keyCode) < 0)
3841
- view.inputState.lastEscPress = 0;
3842
3899
  };
3843
3900
  handlers.touchstart = (view, e) => {
3844
3901
  view.inputState.lastTouchTime = Date.now();
@@ -4140,13 +4197,23 @@ handlers.compositionend = view => {
4140
4197
  view.inputState.compositionPendingKey = true;
4141
4198
  view.inputState.compositionPendingChange = view.observer.pendingRecords().length > 0;
4142
4199
  view.inputState.compositionFirstChange = null;
4143
- if (browser.chrome && browser.android)
4200
+ if (browser.chrome && browser.android) {
4201
+ // Delay flushing for a bit on Android because it'll often fire a
4202
+ // bunch of contradictory changes in a row at end of compositon
4144
4203
  view.observer.flushSoon();
4145
- setTimeout(() => {
4146
- // Force the composition state to be cleared if it hasn't already been
4147
- if (view.inputState.composing < 0 && view.docView.compositionDeco.size)
4148
- view.update([]);
4149
- }, 50);
4204
+ }
4205
+ else if (view.inputState.compositionPendingChange) {
4206
+ // If we found pending records, schedule a flush.
4207
+ Promise.resolve().then(() => view.observer.flush());
4208
+ }
4209
+ else {
4210
+ // Otherwise, make sure that, if no changes come in soon, the
4211
+ // composition view is cleared.
4212
+ setTimeout(() => {
4213
+ if (view.inputState.composing < 0 && view.docView.compositionDeco.size)
4214
+ view.update([]);
4215
+ }, 50);
4216
+ }
4150
4217
  };
4151
4218
  handlers.contextmenu = view => {
4152
4219
  view.inputState.lastContextMenu = Date.now();
@@ -4485,7 +4552,8 @@ class HeightMapGap extends HeightMap {
4485
4552
  if (oracle.lineWrapping) {
4486
4553
  let totalPerLine = Math.min(this.height, oracle.lineHeight * lines);
4487
4554
  perLine = totalPerLine / lines;
4488
- perChar = (this.height - totalPerLine) / (this.length - lines - 1);
4555
+ if (this.length > lines + 1)
4556
+ perChar = (this.height - totalPerLine) / (this.length - lines - 1);
4489
4557
  }
4490
4558
  else {
4491
4559
  perLine = this.height / lines;
@@ -5501,16 +5569,16 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
5501
5569
  "&dark .cm-selectionBackground": {
5502
5570
  background: "#222"
5503
5571
  },
5504
- "&light.cm-focused .cm-selectionBackground": {
5572
+ "&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
5505
5573
  background: "#d7d4f0"
5506
5574
  },
5507
- "&dark.cm-focused .cm-selectionBackground": {
5575
+ "&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
5508
5576
  background: "#233"
5509
5577
  },
5510
5578
  ".cm-cursorLayer": {
5511
5579
  pointerEvents: "none"
5512
5580
  },
5513
- "&.cm-focused .cm-cursorLayer": {
5581
+ "&.cm-focused > .cm-scroller > .cm-cursorLayer": {
5514
5582
  animation: "steps(1) cm-blink 1.2s infinite"
5515
5583
  },
5516
5584
  // Two animations defined so that we can switch between them to
@@ -5532,7 +5600,7 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
5532
5600
  ".cm-dropCursor": {
5533
5601
  position: "absolute"
5534
5602
  },
5535
- "&.cm-focused .cm-cursor": {
5603
+ "&.cm-focused > .cm-scroller > .cm-cursorLayer .cm-cursor": {
5536
5604
  display: "block"
5537
5605
  },
5538
5606
  "&light .cm-activeLine": { backgroundColor: "#cceeff44" },
@@ -8804,6 +8872,11 @@ class HoverTooltipHost {
8804
8872
  update(update) {
8805
8873
  this.manager.update(update);
8806
8874
  }
8875
+ destroy() {
8876
+ var _a;
8877
+ for (let t of this.manager.tooltipViews)
8878
+ (_a = t.destroy) === null || _a === void 0 ? void 0 : _a.call(t);
8879
+ }
8807
8880
  }
8808
8881
  const showHoverTooltipHost = /*@__PURE__*/showTooltip.compute([showHoverTooltip], state => {
8809
8882
  let tooltips = state.facet(showHoverTooltip).filter(t => t);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.9.4",
3
+ "version": "6.9.6",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",