@codemirror/view 0.19.39 → 0.19.40

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,21 @@
1
+ ## 0.19.40 (2022-01-19)
2
+
3
+ ### Bug fixes
4
+
5
+ Make composition input properly appear at secondary cursors (except when those are in the DOM node with the composition, in which case the browser won't allow us to intervene without aborting the composition).
6
+
7
+ Fix a bug that cause the editor to get confused about which content was visible after scrolling something into view.
8
+
9
+ Fix a bug where the dummy elements rendered around widgets could end up in a separate set of wrapping marks, and thus become visible.
10
+
11
+ `EditorView.moveVertically` now preserves the `assoc` property of the input range.
12
+
13
+ Get rid of gaps between selection elements drawn by `drawSelection`.
14
+
15
+ Fix an issue where replacing text next to a widget might leak bogus zero-width spaces into the document.
16
+
17
+ Avoid browser selection mishandling when a focused view has `setState` called by eagerly refocusing it.
18
+
1
19
  ## 0.19.39 (2022-01-06)
2
20
 
3
21
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -1237,7 +1237,7 @@ function widgetsEq(a, b) {
1237
1237
  }
1238
1238
  function addRange(from, to, ranges, margin = 0) {
1239
1239
  let last = ranges.length - 1;
1240
- if (last >= 0 && ranges[last] + margin > from)
1240
+ if (last >= 0 && ranges[last] + margin >= from)
1241
1241
  ranges[last] = Math.max(ranges[last], to);
1242
1242
  else
1243
1243
  ranges.push(from, to);
@@ -1518,7 +1518,7 @@ class ContentBuilder {
1518
1518
  }
1519
1519
  }
1520
1520
  let take = Math.min(this.text.length - this.textOff, length, 512 /* Chunk */);
1521
- this.flushBuffer(active);
1521
+ this.flushBuffer(active.slice(0, openStart));
1522
1522
  this.getLine().append(wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
1523
1523
  this.atCursorPos = true;
1524
1524
  this.textOff += take;
@@ -2332,6 +2332,15 @@ class DOMReader {
2332
2332
  this.findPointBefore(parent, end);
2333
2333
  return this;
2334
2334
  }
2335
+ readTextNode(node) {
2336
+ var _a, _b;
2337
+ let text = node.nodeValue;
2338
+ if (/^\u200b/.test(text) && ((_a = node.previousSibling) === null || _a === void 0 ? void 0 : _a.contentEditable) == "false")
2339
+ text = text.slice(1);
2340
+ if (/\u200b$/.test(text) && ((_b = node.nextSibling) === null || _b === void 0 ? void 0 : _b.contentEditable) == "false")
2341
+ text = text.slice(0, text.length - 1);
2342
+ return text;
2343
+ }
2335
2344
  readNode(node) {
2336
2345
  if (node.cmIgnore)
2337
2346
  return;
@@ -2341,7 +2350,7 @@ class DOMReader {
2341
2350
  if (fromView != null)
2342
2351
  text = fromView.sliceString(0, undefined, this.lineBreak);
2343
2352
  else if (node.nodeType == 3)
2344
- text = node.nodeValue;
2353
+ text = this.readTextNode(node);
2345
2354
  else if (node.nodeName == "BR")
2346
2355
  text = node.nextSibling ? this.lineBreak : "";
2347
2356
  else if (node.nodeType == 1)
@@ -2768,39 +2777,45 @@ class BlockGapWidget extends WidgetType {
2768
2777
  }
2769
2778
  get estimatedHeight() { return this.height; }
2770
2779
  }
2771
- function computeCompositionDeco(view, changes) {
2780
+ function compositionSurroundingNode(view) {
2772
2781
  let sel = view.observer.selectionRange;
2773
2782
  let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
2774
2783
  if (!textNode)
2775
- return Decoration.none;
2784
+ return null;
2776
2785
  let cView = view.docView.nearest(textNode);
2777
2786
  if (!cView)
2778
- return Decoration.none;
2779
- let from, to, topNode = textNode;
2787
+ return null;
2780
2788
  if (cView instanceof LineView) {
2789
+ let topNode = textNode;
2781
2790
  while (topNode.parentNode != cView.dom)
2782
2791
  topNode = topNode.parentNode;
2783
2792
  let prev = topNode.previousSibling;
2784
2793
  while (prev && !ContentView.get(prev))
2785
2794
  prev = prev.previousSibling;
2786
- from = to = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
2795
+ let pos = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
2796
+ return { from: pos, to: pos, node: topNode, text: textNode };
2787
2797
  }
2788
2798
  else {
2789
2799
  for (;;) {
2790
2800
  let { parent } = cView;
2791
2801
  if (!parent)
2792
- return Decoration.none;
2802
+ return null;
2793
2803
  if (parent instanceof LineView)
2794
2804
  break;
2795
2805
  cView = parent;
2796
2806
  }
2797
- from = cView.posAtStart;
2798
- to = from + cView.length;
2799
- topNode = cView.dom;
2807
+ let from = cView.posAtStart;
2808
+ return { from, to: from + cView.length, node: cView.dom, text: textNode };
2800
2809
  }
2810
+ }
2811
+ function computeCompositionDeco(view, changes) {
2812
+ let surrounding = compositionSurroundingNode(view);
2813
+ if (!surrounding)
2814
+ return Decoration.none;
2815
+ let { from, to, node, text: textNode } = surrounding;
2801
2816
  let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
2802
- let { state } = view, text = topNode.nodeType == 3 ? topNode.nodeValue :
2803
- new DOMReader([], view).readRange(topNode.firstChild, null).text;
2817
+ let { state } = view, text = node.nodeType == 3 ? node.nodeValue :
2818
+ new DOMReader([], view).readRange(node.firstChild, null).text;
2804
2819
  if (newTo - newFrom < text.length) {
2805
2820
  if (state.sliceDoc(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
2806
2821
  newTo = newFrom + text.length;
@@ -2812,7 +2827,7 @@ function computeCompositionDeco(view, changes) {
2812
2827
  else if (state.sliceDoc(newFrom, newTo) != text) {
2813
2828
  return Decoration.none;
2814
2829
  }
2815
- return Decoration.set(Decoration.replace({ widget: new CompositionWidget(topNode, textNode) }).range(newFrom, newTo));
2830
+ return Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode) }).range(newFrom, newTo));
2816
2831
  }
2817
2832
  class CompositionWidget extends WidgetType {
2818
2833
  constructor(top, text) {
@@ -3149,7 +3164,7 @@ function byGroup(view, pos, start) {
3149
3164
  function moveVertically(view, start, forward, distance) {
3150
3165
  let startPos = start.head, dir = forward ? 1 : -1;
3151
3166
  if (startPos == (forward ? view.state.doc.length : 0))
3152
- return state.EditorSelection.cursor(startPos);
3167
+ return state.EditorSelection.cursor(startPos, start.assoc);
3153
3168
  let goal = start.goalColumn, startY;
3154
3169
  let rect = view.contentDOM.getBoundingClientRect();
3155
3170
  let startCoords = view.coordsAtPos(startPos), docTop = view.documentTop;
@@ -3170,7 +3185,7 @@ function moveVertically(view, start, forward, distance) {
3170
3185
  let curY = startY + (dist + extra) * dir;
3171
3186
  let pos = posAtCoords(view, { x: resolvedGoal, y: curY }, false, dir);
3172
3187
  if (curY < rect.top || curY > rect.bottom || (dir < 0 ? pos < startPos : pos > startPos))
3173
- return state.EditorSelection.cursor(pos, undefined, undefined, goal);
3188
+ return state.EditorSelection.cursor(pos, start.assoc, undefined, goal);
3174
3189
  }
3175
3190
  }
3176
3191
  function skipAtoms(view, oldPos, pos) {
@@ -4694,7 +4709,8 @@ class ViewState {
4694
4709
  }
4695
4710
  }
4696
4711
  // Pixel viewport
4697
- let pixelViewport = this.printing ? { top: -1e8, bottom: 1e8, left: -1e8, right: 1e8 } : visiblePixelRange(dom, this.paddingTop);
4712
+ let pixelViewport = this.printing ? { top: -1e8, bottom: 1e8, left: -1e8, right: 1e8 }
4713
+ : visiblePixelRange(dom, this.paddingTop);
4698
4714
  let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
4699
4715
  this.pixelViewport = pixelViewport;
4700
4716
  let inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
@@ -5637,19 +5653,47 @@ function applyDOMChange(view, start, end, typeOver) {
5637
5653
  view.inputState.composing++;
5638
5654
  let tr;
5639
5655
  if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
5640
- (!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length)) {
5656
+ (!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length) &&
5657
+ view.inputState.composing < 0) {
5641
5658
  let before = sel.from < change.from ? startState.sliceDoc(sel.from, change.from) : "";
5642
5659
  let after = sel.to > change.to ? startState.sliceDoc(change.to, sel.to) : "";
5643
- tr = startState.replaceSelection(view.state.toText(before + change.insert.sliceString(0, undefined, view.state.lineBreak) +
5644
- after));
5660
+ tr = startState.replaceSelection(view.state.toText(before + change.insert.sliceString(0, undefined, view.state.lineBreak) + after));
5645
5661
  }
5646
5662
  else {
5647
5663
  let changes = startState.changes(change);
5648
- tr = {
5649
- changes,
5650
- selection: newSel && !startState.selection.main.eq(newSel.main) && newSel.main.to <= changes.newLength
5651
- ? startState.selection.replaceRange(newSel.main) : undefined
5652
- };
5664
+ let mainSel = newSel && !startState.selection.main.eq(newSel.main) && newSel.main.to <= changes.newLength
5665
+ ? newSel.main : undefined;
5666
+ // Try to apply a composition change to all cursors
5667
+ if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
5668
+ change.to <= sel.to && change.to >= sel.to - 10) {
5669
+ let replaced = view.state.sliceDoc(change.from, change.to);
5670
+ let compositionRange = compositionSurroundingNode(view) || view.state.doc.lineAt(sel.head);
5671
+ let offset = sel.to - change.to, size = sel.to - sel.from;
5672
+ tr = startState.changeByRange(range => {
5673
+ if (range.from == sel.from && range.to == sel.to)
5674
+ return { changes, range: mainSel || range.map(changes) };
5675
+ let to = range.to - offset, from = to - replaced.length;
5676
+ if (range.to - range.from != size || view.state.sliceDoc(from, to) != replaced ||
5677
+ // Unfortunately, there's no way to make multiple
5678
+ // changes in the same node work without aborting
5679
+ // composition, so cursors in the composition range are
5680
+ // ignored.
5681
+ compositionRange && range.to >= compositionRange.from && range.from <= compositionRange.to)
5682
+ return { range };
5683
+ let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
5684
+ return {
5685
+ changes: rangeChanges,
5686
+ range: !mainSel ? range.map(rangeChanges) :
5687
+ state.EditorSelection.range(Math.max(0, mainSel.anchor + selOff), Math.max(0, mainSel.head + selOff))
5688
+ };
5689
+ });
5690
+ }
5691
+ else {
5692
+ tr = {
5693
+ changes,
5694
+ selection: mainSel && startState.selection.replaceRange(mainSel)
5695
+ };
5696
+ }
5653
5697
  }
5654
5698
  let userEvent = "input.type";
5655
5699
  if (view.composing) {
@@ -5920,6 +5964,7 @@ class EditorView {
5920
5964
  return;
5921
5965
  }
5922
5966
  this.updateState = 2 /* Updating */;
5967
+ let hadFocus = this.hasFocus;
5923
5968
  try {
5924
5969
  for (let plugin of this.plugins)
5925
5970
  plugin.destroy(this);
@@ -5937,6 +5982,8 @@ class EditorView {
5937
5982
  finally {
5938
5983
  this.updateState = 0 /* Idle */;
5939
5984
  }
5985
+ if (hadFocus)
5986
+ this.focus();
5940
5987
  this.requestMeasure();
5941
5988
  }
5942
5989
  updatePlugins(update) {
@@ -6006,7 +6053,7 @@ class EditorView {
6006
6053
  return BadMeasure;
6007
6054
  }
6008
6055
  });
6009
- let update = new ViewUpdate(this, this.state), redrawn = false;
6056
+ let update = new ViewUpdate(this, this.state), redrawn = false, scrolled = false;
6010
6057
  update.flags |= changed;
6011
6058
  if (!updated)
6012
6059
  updated = update;
@@ -6033,11 +6080,12 @@ class EditorView {
6033
6080
  if (this.viewState.scrollTarget) {
6034
6081
  this.docView.scrollIntoView(this.viewState.scrollTarget);
6035
6082
  this.viewState.scrollTarget = null;
6083
+ scrolled = true;
6036
6084
  }
6037
6085
  if (redrawn)
6038
6086
  this.docView.updateSelection(true);
6039
6087
  if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
6040
- this.measureRequests.length == 0)
6088
+ !scrolled && this.measureRequests.length == 0)
6041
6089
  break;
6042
6090
  }
6043
6091
  }
@@ -6173,7 +6221,7 @@ class EditorView {
6173
6221
  (`view.contentDOM.getBoundingClientRect().top`) to limit layout
6174
6222
  queries.
6175
6223
 
6176
- *Deprecated: use `blockAtHeight` instead.*
6224
+ *Deprecated: use `elementAtHeight` instead.*
6177
6225
  */
6178
6226
  blockAtHeight(height, docTop) {
6179
6227
  let top = ensureTop(docTop, this);
@@ -7018,7 +7066,7 @@ function measureRange(view, range) {
7018
7066
  return pieces(top).concat(between).concat(pieces(bottom));
7019
7067
  }
7020
7068
  function piece(left, top, right, bottom) {
7021
- return new Piece(left - base.left, top - base.top, right - left, bottom - top, "cm-selectionBackground");
7069
+ return new Piece(left - base.left, top - base.top - 0.01 /* Epsilon */, right - left, bottom - top + 0.01 /* Epsilon */, "cm-selectionBackground");
7022
7070
  }
7023
7071
  function pieces({ top, bottom, horizontal }) {
7024
7072
  let pieces = [];
package/dist/index.d.ts CHANGED
@@ -802,7 +802,7 @@ declare class EditorView {
802
802
  (`view.contentDOM.getBoundingClientRect().top`) to limit layout
803
803
  queries.
804
804
 
805
- *Deprecated: use `blockAtHeight` instead.*
805
+ *Deprecated: use `elementAtHeight` instead.*
806
806
  */
807
807
  blockAtHeight(height: number, docTop?: number): BlockInfo;
808
808
  /**
package/dist/index.js CHANGED
@@ -1233,7 +1233,7 @@ function widgetsEq(a, b) {
1233
1233
  }
1234
1234
  function addRange(from, to, ranges, margin = 0) {
1235
1235
  let last = ranges.length - 1;
1236
- if (last >= 0 && ranges[last] + margin > from)
1236
+ if (last >= 0 && ranges[last] + margin >= from)
1237
1237
  ranges[last] = Math.max(ranges[last], to);
1238
1238
  else
1239
1239
  ranges.push(from, to);
@@ -1514,7 +1514,7 @@ class ContentBuilder {
1514
1514
  }
1515
1515
  }
1516
1516
  let take = Math.min(this.text.length - this.textOff, length, 512 /* Chunk */);
1517
- this.flushBuffer(active);
1517
+ this.flushBuffer(active.slice(0, openStart));
1518
1518
  this.getLine().append(wrapMarks(new TextView(this.text.slice(this.textOff, this.textOff + take)), active), openStart);
1519
1519
  this.atCursorPos = true;
1520
1520
  this.textOff += take;
@@ -2327,6 +2327,15 @@ class DOMReader {
2327
2327
  this.findPointBefore(parent, end);
2328
2328
  return this;
2329
2329
  }
2330
+ readTextNode(node) {
2331
+ var _a, _b;
2332
+ let text = node.nodeValue;
2333
+ if (/^\u200b/.test(text) && ((_a = node.previousSibling) === null || _a === void 0 ? void 0 : _a.contentEditable) == "false")
2334
+ text = text.slice(1);
2335
+ if (/\u200b$/.test(text) && ((_b = node.nextSibling) === null || _b === void 0 ? void 0 : _b.contentEditable) == "false")
2336
+ text = text.slice(0, text.length - 1);
2337
+ return text;
2338
+ }
2330
2339
  readNode(node) {
2331
2340
  if (node.cmIgnore)
2332
2341
  return;
@@ -2336,7 +2345,7 @@ class DOMReader {
2336
2345
  if (fromView != null)
2337
2346
  text = fromView.sliceString(0, undefined, this.lineBreak);
2338
2347
  else if (node.nodeType == 3)
2339
- text = node.nodeValue;
2348
+ text = this.readTextNode(node);
2340
2349
  else if (node.nodeName == "BR")
2341
2350
  text = node.nextSibling ? this.lineBreak : "";
2342
2351
  else if (node.nodeType == 1)
@@ -2763,39 +2772,45 @@ class BlockGapWidget extends WidgetType {
2763
2772
  }
2764
2773
  get estimatedHeight() { return this.height; }
2765
2774
  }
2766
- function computeCompositionDeco(view, changes) {
2775
+ function compositionSurroundingNode(view) {
2767
2776
  let sel = view.observer.selectionRange;
2768
2777
  let textNode = sel.focusNode && nearbyTextNode(sel.focusNode, sel.focusOffset, 0);
2769
2778
  if (!textNode)
2770
- return Decoration.none;
2779
+ return null;
2771
2780
  let cView = view.docView.nearest(textNode);
2772
2781
  if (!cView)
2773
- return Decoration.none;
2774
- let from, to, topNode = textNode;
2782
+ return null;
2775
2783
  if (cView instanceof LineView) {
2784
+ let topNode = textNode;
2776
2785
  while (topNode.parentNode != cView.dom)
2777
2786
  topNode = topNode.parentNode;
2778
2787
  let prev = topNode.previousSibling;
2779
2788
  while (prev && !ContentView.get(prev))
2780
2789
  prev = prev.previousSibling;
2781
- from = to = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
2790
+ let pos = prev ? ContentView.get(prev).posAtEnd : cView.posAtStart;
2791
+ return { from: pos, to: pos, node: topNode, text: textNode };
2782
2792
  }
2783
2793
  else {
2784
2794
  for (;;) {
2785
2795
  let { parent } = cView;
2786
2796
  if (!parent)
2787
- return Decoration.none;
2797
+ return null;
2788
2798
  if (parent instanceof LineView)
2789
2799
  break;
2790
2800
  cView = parent;
2791
2801
  }
2792
- from = cView.posAtStart;
2793
- to = from + cView.length;
2794
- topNode = cView.dom;
2802
+ let from = cView.posAtStart;
2803
+ return { from, to: from + cView.length, node: cView.dom, text: textNode };
2795
2804
  }
2805
+ }
2806
+ function computeCompositionDeco(view, changes) {
2807
+ let surrounding = compositionSurroundingNode(view);
2808
+ if (!surrounding)
2809
+ return Decoration.none;
2810
+ let { from, to, node, text: textNode } = surrounding;
2796
2811
  let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
2797
- let { state } = view, text = topNode.nodeType == 3 ? topNode.nodeValue :
2798
- new DOMReader([], view).readRange(topNode.firstChild, null).text;
2812
+ let { state } = view, text = node.nodeType == 3 ? node.nodeValue :
2813
+ new DOMReader([], view).readRange(node.firstChild, null).text;
2799
2814
  if (newTo - newFrom < text.length) {
2800
2815
  if (state.sliceDoc(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
2801
2816
  newTo = newFrom + text.length;
@@ -2807,7 +2822,7 @@ function computeCompositionDeco(view, changes) {
2807
2822
  else if (state.sliceDoc(newFrom, newTo) != text) {
2808
2823
  return Decoration.none;
2809
2824
  }
2810
- return Decoration.set(Decoration.replace({ widget: new CompositionWidget(topNode, textNode) }).range(newFrom, newTo));
2825
+ return Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode) }).range(newFrom, newTo));
2811
2826
  }
2812
2827
  class CompositionWidget extends WidgetType {
2813
2828
  constructor(top, text) {
@@ -3144,7 +3159,7 @@ function byGroup(view, pos, start) {
3144
3159
  function moveVertically(view, start, forward, distance) {
3145
3160
  let startPos = start.head, dir = forward ? 1 : -1;
3146
3161
  if (startPos == (forward ? view.state.doc.length : 0))
3147
- return EditorSelection.cursor(startPos);
3162
+ return EditorSelection.cursor(startPos, start.assoc);
3148
3163
  let goal = start.goalColumn, startY;
3149
3164
  let rect = view.contentDOM.getBoundingClientRect();
3150
3165
  let startCoords = view.coordsAtPos(startPos), docTop = view.documentTop;
@@ -3165,7 +3180,7 @@ function moveVertically(view, start, forward, distance) {
3165
3180
  let curY = startY + (dist + extra) * dir;
3166
3181
  let pos = posAtCoords(view, { x: resolvedGoal, y: curY }, false, dir);
3167
3182
  if (curY < rect.top || curY > rect.bottom || (dir < 0 ? pos < startPos : pos > startPos))
3168
- return EditorSelection.cursor(pos, undefined, undefined, goal);
3183
+ return EditorSelection.cursor(pos, start.assoc, undefined, goal);
3169
3184
  }
3170
3185
  }
3171
3186
  function skipAtoms(view, oldPos, pos) {
@@ -4688,7 +4703,8 @@ class ViewState {
4688
4703
  }
4689
4704
  }
4690
4705
  // Pixel viewport
4691
- let pixelViewport = this.printing ? { top: -1e8, bottom: 1e8, left: -1e8, right: 1e8 } : visiblePixelRange(dom, this.paddingTop);
4706
+ let pixelViewport = this.printing ? { top: -1e8, bottom: 1e8, left: -1e8, right: 1e8 }
4707
+ : visiblePixelRange(dom, this.paddingTop);
4692
4708
  let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
4693
4709
  this.pixelViewport = pixelViewport;
4694
4710
  let inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
@@ -5631,19 +5647,47 @@ function applyDOMChange(view, start, end, typeOver) {
5631
5647
  view.inputState.composing++;
5632
5648
  let tr;
5633
5649
  if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
5634
- (!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length)) {
5650
+ (!newSel || newSel.main.empty && newSel.main.from == change.from + change.insert.length) &&
5651
+ view.inputState.composing < 0) {
5635
5652
  let before = sel.from < change.from ? startState.sliceDoc(sel.from, change.from) : "";
5636
5653
  let after = sel.to > change.to ? startState.sliceDoc(change.to, sel.to) : "";
5637
- tr = startState.replaceSelection(view.state.toText(before + change.insert.sliceString(0, undefined, view.state.lineBreak) +
5638
- after));
5654
+ tr = startState.replaceSelection(view.state.toText(before + change.insert.sliceString(0, undefined, view.state.lineBreak) + after));
5639
5655
  }
5640
5656
  else {
5641
5657
  let changes = startState.changes(change);
5642
- tr = {
5643
- changes,
5644
- selection: newSel && !startState.selection.main.eq(newSel.main) && newSel.main.to <= changes.newLength
5645
- ? startState.selection.replaceRange(newSel.main) : undefined
5646
- };
5658
+ let mainSel = newSel && !startState.selection.main.eq(newSel.main) && newSel.main.to <= changes.newLength
5659
+ ? newSel.main : undefined;
5660
+ // Try to apply a composition change to all cursors
5661
+ if (startState.selection.ranges.length > 1 && view.inputState.composing >= 0 &&
5662
+ change.to <= sel.to && change.to >= sel.to - 10) {
5663
+ let replaced = view.state.sliceDoc(change.from, change.to);
5664
+ let compositionRange = compositionSurroundingNode(view) || view.state.doc.lineAt(sel.head);
5665
+ let offset = sel.to - change.to, size = sel.to - sel.from;
5666
+ tr = startState.changeByRange(range => {
5667
+ if (range.from == sel.from && range.to == sel.to)
5668
+ return { changes, range: mainSel || range.map(changes) };
5669
+ let to = range.to - offset, from = to - replaced.length;
5670
+ if (range.to - range.from != size || view.state.sliceDoc(from, to) != replaced ||
5671
+ // Unfortunately, there's no way to make multiple
5672
+ // changes in the same node work without aborting
5673
+ // composition, so cursors in the composition range are
5674
+ // ignored.
5675
+ compositionRange && range.to >= compositionRange.from && range.from <= compositionRange.to)
5676
+ return { range };
5677
+ let rangeChanges = startState.changes({ from, to, insert: change.insert }), selOff = range.to - sel.to;
5678
+ return {
5679
+ changes: rangeChanges,
5680
+ range: !mainSel ? range.map(rangeChanges) :
5681
+ EditorSelection.range(Math.max(0, mainSel.anchor + selOff), Math.max(0, mainSel.head + selOff))
5682
+ };
5683
+ });
5684
+ }
5685
+ else {
5686
+ tr = {
5687
+ changes,
5688
+ selection: mainSel && startState.selection.replaceRange(mainSel)
5689
+ };
5690
+ }
5647
5691
  }
5648
5692
  let userEvent = "input.type";
5649
5693
  if (view.composing) {
@@ -5914,6 +5958,7 @@ class EditorView {
5914
5958
  return;
5915
5959
  }
5916
5960
  this.updateState = 2 /* Updating */;
5961
+ let hadFocus = this.hasFocus;
5917
5962
  try {
5918
5963
  for (let plugin of this.plugins)
5919
5964
  plugin.destroy(this);
@@ -5931,6 +5976,8 @@ class EditorView {
5931
5976
  finally {
5932
5977
  this.updateState = 0 /* Idle */;
5933
5978
  }
5979
+ if (hadFocus)
5980
+ this.focus();
5934
5981
  this.requestMeasure();
5935
5982
  }
5936
5983
  updatePlugins(update) {
@@ -6000,7 +6047,7 @@ class EditorView {
6000
6047
  return BadMeasure;
6001
6048
  }
6002
6049
  });
6003
- let update = new ViewUpdate(this, this.state), redrawn = false;
6050
+ let update = new ViewUpdate(this, this.state), redrawn = false, scrolled = false;
6004
6051
  update.flags |= changed;
6005
6052
  if (!updated)
6006
6053
  updated = update;
@@ -6027,11 +6074,12 @@ class EditorView {
6027
6074
  if (this.viewState.scrollTarget) {
6028
6075
  this.docView.scrollIntoView(this.viewState.scrollTarget);
6029
6076
  this.viewState.scrollTarget = null;
6077
+ scrolled = true;
6030
6078
  }
6031
6079
  if (redrawn)
6032
6080
  this.docView.updateSelection(true);
6033
6081
  if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
6034
- this.measureRequests.length == 0)
6082
+ !scrolled && this.measureRequests.length == 0)
6035
6083
  break;
6036
6084
  }
6037
6085
  }
@@ -6167,7 +6215,7 @@ class EditorView {
6167
6215
  (`view.contentDOM.getBoundingClientRect().top`) to limit layout
6168
6216
  queries.
6169
6217
 
6170
- *Deprecated: use `blockAtHeight` instead.*
6218
+ *Deprecated: use `elementAtHeight` instead.*
6171
6219
  */
6172
6220
  blockAtHeight(height, docTop) {
6173
6221
  let top = ensureTop(docTop, this);
@@ -7012,7 +7060,7 @@ function measureRange(view, range) {
7012
7060
  return pieces(top).concat(between).concat(pieces(bottom));
7013
7061
  }
7014
7062
  function piece(left, top, right, bottom) {
7015
- return new Piece(left - base.left, top - base.top, right - left, bottom - top, "cm-selectionBackground");
7063
+ return new Piece(left - base.left, top - base.top - 0.01 /* Epsilon */, right - left, bottom - top + 0.01 /* Epsilon */, "cm-selectionBackground");
7016
7064
  }
7017
7065
  function pieces({ top, bottom, horizontal }) {
7018
7066
  let pieces = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "0.19.39",
3
+ "version": "0.19.40",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",