@codemirror/view 6.13.2 → 6.14.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,29 @@
1
+ ## 6.14.1 (2023-07-06)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue where scrolling up through line-wrapped text would sometimes cause the scroll position to pop down.
6
+
7
+ Fix an issue where clicking wouldn't focus the editor on Firefox when it was in an iframe and already the active element of the frame.
8
+
9
+ Fix a bug that could cause compositions to be disrupted because their surrounding DOM was repurposed for some other piece of content.
10
+
11
+ Fix a bug where adding content to the editor could inappropriately move the scroll position.
12
+
13
+ Extend detection of Enter presses on Android to `beforeInput` events with an `"insertLineBreak"` type.
14
+
15
+ ## 6.14.0 (2023-06-23)
16
+
17
+ ### Bug fixes
18
+
19
+ When dragging text inside the editor, look at the state of Ctrl (or Alt on macOS) at the time of the drop, not the start of drag, to determine whether to move or copy the text.
20
+
21
+ Fix an issue where having a bunch of padding on lines could cause vertical cursor motion and `posAtCoords` to jump over lines.
22
+
23
+ ### New features
24
+
25
+ Block widget decorations can now be given an `inlineOrder` option to make them appear in the same ordering as surrounding inline widgets.
26
+
1
27
  ## 6.13.2 (2023-06-13)
2
28
 
3
29
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -320,6 +320,9 @@ function atElementStart(doc, selection) {
320
320
  }
321
321
  }
322
322
  }
323
+ function isScrolledToBottom(elt) {
324
+ return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4);
325
+ }
323
326
 
324
327
  class DOMPos {
325
328
  constructor(node, offset, precise = true) {
@@ -362,7 +365,7 @@ class ContentView {
362
365
  let prev = null, next;
363
366
  for (let child of this.children) {
364
367
  if (child.dirty) {
365
- if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
368
+ if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild) && next != view.docView.compositionNode) {
366
369
  let contentView = ContentView.get(next);
367
370
  if (!contentView || !contentView.parent && contentView.canReuseDOM(child))
368
371
  child.reuseDOM(next);
@@ -1327,7 +1330,9 @@ class Decoration extends state.RangeValue {
1327
1330
  */
1328
1331
  static widget(spec) {
1329
1332
  let side = Math.max(-10000, Math.min(10000, spec.side || 0)), block = !!spec.block;
1330
- side += block ? (side > 0 ? 300000000 /* BlockAfter */ : -400000000 /* BlockBefore */) : (side > 0 ? 100000000 /* InlineAfter */ : -100000000 /* InlineBefore */);
1333
+ side += (block && !spec.inlineOrder)
1334
+ ? (side > 0 ? 300000000 /* BlockAfter */ : -400000000 /* BlockBefore */)
1335
+ : (side > 0 ? 100000000 /* InlineAfter */ : -100000000 /* InlineBefore */);
1331
1336
  return new PointDecoration(spec, side, side, block, spec.widget || null, false);
1332
1337
  }
1333
1338
  /**
@@ -2599,6 +2604,7 @@ class DocView extends ContentView {
2599
2604
  super();
2600
2605
  this.view = view;
2601
2606
  this.compositionDeco = Decoration.none;
2607
+ this.compositionNode = null;
2602
2608
  this.decorations = [];
2603
2609
  this.dynamicDecorationMap = [];
2604
2610
  // Track a minimum width for the editor. When measuring sizes in
@@ -2638,10 +2644,8 @@ class DocView extends ContentView {
2638
2644
  this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
2639
2645
  }
2640
2646
  }
2641
- if (this.view.inputState.composing < 0)
2642
- this.compositionDeco = Decoration.none;
2643
- else if (update.transactions.length || this.dirty)
2644
- this.compositionDeco = computeCompositionDeco(this.view, update.changes);
2647
+ ({ deco: this.compositionDeco, node: this.compositionNode } =
2648
+ this.view.inputState.composing < 0 ? noComp : computeCompositionDeco(this.view, update.changes));
2645
2649
  // When the DOM nodes around the selection are moved to another
2646
2650
  // parent, Chrome sometimes reports a different selection through
2647
2651
  // getSelection than the one that it actually shows to the user.
@@ -3029,10 +3033,11 @@ function compositionSurroundingNode(view) {
3029
3033
  return { from, to: from + cView.length, node: cView.dom, text: textNode };
3030
3034
  }
3031
3035
  }
3036
+ const noComp = { deco: Decoration.none, node: null };
3032
3037
  function computeCompositionDeco(view, changes) {
3033
3038
  let surrounding = compositionSurroundingNode(view);
3034
3039
  if (!surrounding)
3035
- return Decoration.none;
3040
+ return noComp;
3036
3041
  let { from, to, node, text: textNode } = surrounding;
3037
3042
  let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
3038
3043
  let { state } = view, reader = new DOMReader([], state);
@@ -3042,25 +3047,26 @@ function computeCompositionDeco(view, changes) {
3042
3047
  reader.readRange(node.firstChild, null);
3043
3048
  let { text } = reader;
3044
3049
  if (text.indexOf(LineBreakPlaceholder) > -1)
3045
- return Decoration.none; // Don't try to preserve multi-line compositions
3050
+ return noComp; // Don't try to preserve multi-line compositions
3046
3051
  if (newTo - newFrom < text.length) {
3047
3052
  if (state.doc.sliceString(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
3048
3053
  newTo = newFrom + text.length;
3049
3054
  else if (state.doc.sliceString(Math.max(0, newTo - text.length), newTo) == text)
3050
3055
  newFrom = newTo - text.length;
3051
3056
  else
3052
- return Decoration.none;
3057
+ return noComp;
3053
3058
  }
3054
3059
  else if (state.doc.sliceString(newFrom, newTo) != text) {
3055
- return Decoration.none;
3060
+ return noComp;
3056
3061
  }
3057
3062
  let topView = ContentView.get(node);
3058
3063
  if (topView instanceof CompositionView)
3059
3064
  topView = topView.widget.topView;
3060
3065
  else if (topView)
3061
3066
  topView.parent = null;
3062
- return Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode, topView), inclusive: true })
3067
+ let deco = Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode, topView), inclusive: true })
3063
3068
  .range(newFrom, newTo));
3069
+ return { deco, node };
3064
3070
  }
3065
3071
  class CompositionWidget extends WidgetType {
3066
3072
  constructor(top, text, topView) {
@@ -3272,7 +3278,7 @@ function posAtCoords(view, coords, precise, bias = -1) {
3272
3278
  if (yOffset > docHeight)
3273
3279
  return view.state.doc.length;
3274
3280
  // Scan for a text block near the queried y position
3275
- for (let halfLine = view.defaultLineHeight / 2, bounced = false;;) {
3281
+ for (let halfLine = view.viewState.heightOracle.textHeight / 2, bounced = false;;) {
3276
3282
  block = view.elementAtHeight(yOffset);
3277
3283
  if (block.type == exports.BlockType.Text)
3278
3284
  break;
@@ -3352,7 +3358,8 @@ function posAtCoords(view, coords, precise, bias = -1) {
3352
3358
  function posAtCoordsImprecise(view, contentRect, block, x, y) {
3353
3359
  let into = Math.round((x - contentRect.left) * view.defaultCharacterWidth);
3354
3360
  if (view.lineWrapping && block.height > view.defaultLineHeight * 1.5) {
3355
- let line = Math.floor((y - block.top) / view.defaultLineHeight);
3361
+ let textHeight = view.viewState.heightOracle.textHeight;
3362
+ let line = Math.floor((y - block.top - (view.defaultLineHeight - textHeight) * 0.5) / textHeight);
3356
3363
  into += line * view.viewState.heightOracle.lineLength;
3357
3364
  }
3358
3365
  let content = view.state.sliceDoc(block.from, block.to);
@@ -3463,7 +3470,7 @@ function moveVertically(view, start, forward, distance) {
3463
3470
  startY = (dir < 0 ? line.top : line.bottom) + docTop;
3464
3471
  }
3465
3472
  let resolvedGoal = rect.left + goal;
3466
- let dist = distance !== null && distance !== void 0 ? distance : (view.defaultLineHeight >> 1);
3473
+ let dist = distance !== null && distance !== void 0 ? distance : (view.viewState.heightOracle.textHeight >> 1);
3467
3474
  for (let extra = 0;; extra += 10) {
3468
3475
  let curY = startY + (dist + extra) * dir;
3469
3476
  let pos = posAtCoords(view, { x: resolvedGoal, y: curY }, false, dir);
@@ -3722,6 +3729,7 @@ class InputState {
3722
3729
  const PendingKeys = [
3723
3730
  { key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
3724
3731
  { key: "Enter", keyCode: 13, inputType: "insertParagraph" },
3732
+ { key: "Enter", keyCode: 13, inputType: "insertLineBreak" },
3725
3733
  { key: "Delete", keyCode: 46, inputType: "deleteContentForward" }
3726
3734
  ];
3727
3735
  const EmacsyPendingKeys = "dthko";
@@ -3746,7 +3754,6 @@ class MouseSelection {
3746
3754
  doc.addEventListener("mouseup", this.up = this.up.bind(this));
3747
3755
  this.extend = startEvent.shiftKey;
3748
3756
  this.multiple = view.state.facet(state.EditorState.allowMultipleSelections) && addsSelectionRange(view, startEvent);
3749
- this.dragMove = dragMovesSelection(view, startEvent);
3750
3757
  this.dragging = isInPrimarySelection(view, startEvent) && getClickType(startEvent) == 1 ? null : false;
3751
3758
  }
3752
3759
  start(event) {
@@ -3967,7 +3974,7 @@ handlers.mousedown = (view, event) => {
3967
3974
  if (!style && event.button == 0)
3968
3975
  style = basicMouseSelection(view, event);
3969
3976
  if (style) {
3970
- let mustFocus = view.root.activeElement != view.contentDOM;
3977
+ let mustFocus = !view.hasFocus;
3971
3978
  view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
3972
3979
  if (mustFocus)
3973
3980
  view.observer.ignore(() => focusPreventScroll(view.contentDOM));
@@ -4084,7 +4091,7 @@ function dropText(view, event, text, direct) {
4084
4091
  let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
4085
4092
  event.preventDefault();
4086
4093
  let { mouseSelection } = view.inputState;
4087
- let del = direct && mouseSelection && mouseSelection.dragging && mouseSelection.dragMove ?
4094
+ let del = direct && mouseSelection && mouseSelection.dragging && dragMovesSelection(view, event) ?
4088
4095
  { from: mouseSelection.dragging.from, to: mouseSelection.dragging.to } : null;
4089
4096
  let ins = { from: dropPos, insert: text };
4090
4097
  let changes = view.state.changes(del ? [del, ins] : ins);
@@ -5164,7 +5171,7 @@ class ViewState {
5164
5171
  let contentChanges = update.changedRanges;
5165
5172
  let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : state.ChangeSet.empty(this.state.doc.length)));
5166
5173
  let prevHeight = this.heightMap.height;
5167
- let scrollAnchor = this.scrolledToBottom ? null : this.lineBlockAtHeight(this.scrollTop);
5174
+ let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.scrollTop);
5168
5175
  this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
5169
5176
  if (this.heightMap.height != prevHeight)
5170
5177
  update.flags |= 2 /* Height */;
@@ -5224,7 +5231,7 @@ class ViewState {
5224
5231
  this.scrollAnchorHeight = -1;
5225
5232
  this.scrollTop = view.scrollDOM.scrollTop;
5226
5233
  }
5227
- this.scrolledToBottom = this.scrollTop > view.scrollDOM.scrollHeight - view.scrollDOM.clientHeight - 4;
5234
+ this.scrolledToBottom = isScrolledToBottom(view.scrollDOM);
5228
5235
  // Pixel viewport
5229
5236
  let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
5230
5237
  let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
@@ -5467,6 +5474,10 @@ class ViewState {
5467
5474
  lineBlockAtHeight(height) {
5468
5475
  return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
5469
5476
  }
5477
+ scrollAnchorAt(scrollTop) {
5478
+ let block = this.lineBlockAtHeight(scrollTop + 8);
5479
+ return block.from >= this.viewport.from || this.viewportLines[0].top - scrollTop > 200 ? block : this.viewportLines[0];
5480
+ }
5470
5481
  elementAtHeight(height) {
5471
5482
  return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.heightOracle, 0, 0), this.scaler);
5472
5483
  }
@@ -6839,22 +6850,23 @@ class EditorView {
6839
6850
  let updated = null;
6840
6851
  let sDOM = this.scrollDOM, { scrollTop } = sDOM;
6841
6852
  let { scrollAnchorPos, scrollAnchorHeight } = this.viewState;
6853
+ if (scrollTop != this.viewState.scrollTop)
6854
+ scrollAnchorHeight = -1;
6842
6855
  this.viewState.scrollAnchorHeight = -1;
6843
- if (scrollAnchorHeight < 0 || scrollTop != this.viewState.scrollTop) {
6844
- if (scrollTop > sDOM.scrollHeight - sDOM.clientHeight - 4) {
6845
- scrollAnchorPos = -1;
6846
- scrollAnchorHeight = this.viewState.heightMap.height;
6847
- }
6848
- else {
6849
- let block = this.viewState.lineBlockAtHeight(scrollTop);
6850
- scrollAnchorPos = block.from;
6851
- scrollAnchorHeight = block.top;
6852
- }
6853
- }
6854
6856
  try {
6855
6857
  for (let i = 0;; i++) {
6858
+ if (scrollAnchorHeight < 0) {
6859
+ if (isScrolledToBottom(sDOM)) {
6860
+ scrollAnchorPos = -1;
6861
+ scrollAnchorHeight = this.viewState.heightMap.height;
6862
+ }
6863
+ else {
6864
+ let block = this.viewState.scrollAnchorAt(scrollTop);
6865
+ scrollAnchorPos = block.from;
6866
+ scrollAnchorHeight = block.top;
6867
+ }
6868
+ }
6856
6869
  this.updateState = 1 /* Measuring */;
6857
- let oldViewport = this.viewport;
6858
6870
  let changed = this.viewState.measure(this);
6859
6871
  if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
6860
6872
  break;
@@ -6877,7 +6889,7 @@ class EditorView {
6877
6889
  return BadMeasure;
6878
6890
  }
6879
6891
  });
6880
- let update = ViewUpdate.create(this, this.state, []), redrawn = false, scrolled = false;
6892
+ let update = ViewUpdate.create(this, this.state, []), redrawn = false;
6881
6893
  update.flags |= changed;
6882
6894
  if (!updated)
6883
6895
  updated = update;
@@ -6901,28 +6913,28 @@ class EditorView {
6901
6913
  logException(this.state, e);
6902
6914
  }
6903
6915
  }
6904
- if (this.viewState.editorHeight) {
6905
- if (this.viewState.scrollTarget) {
6906
- this.docView.scrollIntoView(this.viewState.scrollTarget);
6907
- this.viewState.scrollTarget = null;
6908
- scrolled = true;
6909
- }
6910
- else if (scrollAnchorHeight > -1) {
6911
- let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
6912
- this.viewState.lineBlockAt(scrollAnchorPos).top;
6913
- let diff = newAnchorHeight - scrollAnchorHeight;
6914
- if (diff > 1 || diff < -1) {
6915
- sDOM.scrollTop = scrollTop + diff;
6916
- scrolled = true;
6917
- }
6918
- }
6919
- }
6920
6916
  if (redrawn)
6921
6917
  this.docView.updateSelection(true);
6922
- if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
6923
- !scrolled && this.measureRequests.length == 0)
6918
+ if (!update.viewportChanged && this.measureRequests.length == 0) {
6919
+ if (this.viewState.editorHeight) {
6920
+ if (this.viewState.scrollTarget) {
6921
+ this.docView.scrollIntoView(this.viewState.scrollTarget);
6922
+ this.viewState.scrollTarget = null;
6923
+ continue;
6924
+ }
6925
+ else {
6926
+ let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
6927
+ this.viewState.lineBlockAt(scrollAnchorPos).top;
6928
+ let diff = newAnchorHeight - scrollAnchorHeight;
6929
+ if (diff > 1 || diff < -1) {
6930
+ scrollTop = sDOM.scrollTop = scrollTop + diff;
6931
+ scrollAnchorHeight = -1;
6932
+ continue;
6933
+ }
6934
+ }
6935
+ }
6924
6936
  break;
6925
- scrollAnchorHeight = -1;
6937
+ }
6926
6938
  }
6927
6939
  }
6928
6940
  finally {
package/dist/index.d.cts CHANGED
@@ -72,13 +72,18 @@ interface WidgetDecorationSpec {
72
72
  values will determine their ordering—those with a lower value
73
73
  come first. Defaults to 0. May not be more than 10000 or less
74
74
  than -10000.
75
-
76
- Block widgets are always drawn before inline widgets when side
77
- is non-positive, and after them when side is positive,
78
- regardless of the value of `side`.
79
75
  */
80
76
  side?: number;
81
77
  /**
78
+ By default, to avoid unintended mixing of block and inline
79
+ widgets, block widgets with a positive `side` are always drawn
80
+ after all inline widgets at that position, and those with a
81
+ non-positive side before inline widgets. Setting this option to
82
+ `true` for a block widget will turn this off and cause it to be
83
+ rendered between the inline widgets, ordered by `side`.
84
+ */
85
+ inlineOrder?: boolean;
86
+ /**
82
87
  Determines whether this is a block widgets, which will be drawn
83
88
  between lines, or an inline widget (the default) which is drawn
84
89
  between the surrounding text.
package/dist/index.d.ts CHANGED
@@ -72,13 +72,18 @@ interface WidgetDecorationSpec {
72
72
  values will determine their ordering—those with a lower value
73
73
  come first. Defaults to 0. May not be more than 10000 or less
74
74
  than -10000.
75
-
76
- Block widgets are always drawn before inline widgets when side
77
- is non-positive, and after them when side is positive,
78
- regardless of the value of `side`.
79
75
  */
80
76
  side?: number;
81
77
  /**
78
+ By default, to avoid unintended mixing of block and inline
79
+ widgets, block widgets with a positive `side` are always drawn
80
+ after all inline widgets at that position, and those with a
81
+ non-positive side before inline widgets. Setting this option to
82
+ `true` for a block widget will turn this off and cause it to be
83
+ rendered between the inline widgets, ordered by `side`.
84
+ */
85
+ inlineOrder?: boolean;
86
+ /**
82
87
  Determines whether this is a block widgets, which will be drawn
83
88
  between lines, or an inline widget (the default) which is drawn
84
89
  between the surrounding text.
package/dist/index.js CHANGED
@@ -316,6 +316,9 @@ function atElementStart(doc, selection) {
316
316
  }
317
317
  }
318
318
  }
319
+ function isScrolledToBottom(elt) {
320
+ return elt.scrollTop > Math.max(1, elt.scrollHeight - elt.clientHeight - 4);
321
+ }
319
322
 
320
323
  class DOMPos {
321
324
  constructor(node, offset, precise = true) {
@@ -358,7 +361,7 @@ class ContentView {
358
361
  let prev = null, next;
359
362
  for (let child of this.children) {
360
363
  if (child.dirty) {
361
- if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild)) {
364
+ if (!child.dom && (next = prev ? prev.nextSibling : parent.firstChild) && next != view.docView.compositionNode) {
362
365
  let contentView = ContentView.get(next);
363
366
  if (!contentView || !contentView.parent && contentView.canReuseDOM(child))
364
367
  child.reuseDOM(next);
@@ -1322,7 +1325,9 @@ class Decoration extends RangeValue {
1322
1325
  */
1323
1326
  static widget(spec) {
1324
1327
  let side = Math.max(-10000, Math.min(10000, spec.side || 0)), block = !!spec.block;
1325
- side += block ? (side > 0 ? 300000000 /* BlockAfter */ : -400000000 /* BlockBefore */) : (side > 0 ? 100000000 /* InlineAfter */ : -100000000 /* InlineBefore */);
1328
+ side += (block && !spec.inlineOrder)
1329
+ ? (side > 0 ? 300000000 /* BlockAfter */ : -400000000 /* BlockBefore */)
1330
+ : (side > 0 ? 100000000 /* InlineAfter */ : -100000000 /* InlineBefore */);
1326
1331
  return new PointDecoration(spec, side, side, block, spec.widget || null, false);
1327
1332
  }
1328
1333
  /**
@@ -2593,6 +2598,7 @@ class DocView extends ContentView {
2593
2598
  super();
2594
2599
  this.view = view;
2595
2600
  this.compositionDeco = Decoration.none;
2601
+ this.compositionNode = null;
2596
2602
  this.decorations = [];
2597
2603
  this.dynamicDecorationMap = [];
2598
2604
  // Track a minimum width for the editor. When measuring sizes in
@@ -2632,10 +2638,8 @@ class DocView extends ContentView {
2632
2638
  this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
2633
2639
  }
2634
2640
  }
2635
- if (this.view.inputState.composing < 0)
2636
- this.compositionDeco = Decoration.none;
2637
- else if (update.transactions.length || this.dirty)
2638
- this.compositionDeco = computeCompositionDeco(this.view, update.changes);
2641
+ ({ deco: this.compositionDeco, node: this.compositionNode } =
2642
+ this.view.inputState.composing < 0 ? noComp : computeCompositionDeco(this.view, update.changes));
2639
2643
  // When the DOM nodes around the selection are moved to another
2640
2644
  // parent, Chrome sometimes reports a different selection through
2641
2645
  // getSelection than the one that it actually shows to the user.
@@ -3023,10 +3027,11 @@ function compositionSurroundingNode(view) {
3023
3027
  return { from, to: from + cView.length, node: cView.dom, text: textNode };
3024
3028
  }
3025
3029
  }
3030
+ const noComp = { deco: Decoration.none, node: null };
3026
3031
  function computeCompositionDeco(view, changes) {
3027
3032
  let surrounding = compositionSurroundingNode(view);
3028
3033
  if (!surrounding)
3029
- return Decoration.none;
3034
+ return noComp;
3030
3035
  let { from, to, node, text: textNode } = surrounding;
3031
3036
  let newFrom = changes.mapPos(from, 1), newTo = Math.max(newFrom, changes.mapPos(to, -1));
3032
3037
  let { state } = view, reader = new DOMReader([], state);
@@ -3036,25 +3041,26 @@ function computeCompositionDeco(view, changes) {
3036
3041
  reader.readRange(node.firstChild, null);
3037
3042
  let { text } = reader;
3038
3043
  if (text.indexOf(LineBreakPlaceholder) > -1)
3039
- return Decoration.none; // Don't try to preserve multi-line compositions
3044
+ return noComp; // Don't try to preserve multi-line compositions
3040
3045
  if (newTo - newFrom < text.length) {
3041
3046
  if (state.doc.sliceString(newFrom, Math.min(state.doc.length, newFrom + text.length)) == text)
3042
3047
  newTo = newFrom + text.length;
3043
3048
  else if (state.doc.sliceString(Math.max(0, newTo - text.length), newTo) == text)
3044
3049
  newFrom = newTo - text.length;
3045
3050
  else
3046
- return Decoration.none;
3051
+ return noComp;
3047
3052
  }
3048
3053
  else if (state.doc.sliceString(newFrom, newTo) != text) {
3049
- return Decoration.none;
3054
+ return noComp;
3050
3055
  }
3051
3056
  let topView = ContentView.get(node);
3052
3057
  if (topView instanceof CompositionView)
3053
3058
  topView = topView.widget.topView;
3054
3059
  else if (topView)
3055
3060
  topView.parent = null;
3056
- return Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode, topView), inclusive: true })
3061
+ let deco = Decoration.set(Decoration.replace({ widget: new CompositionWidget(node, textNode, topView), inclusive: true })
3057
3062
  .range(newFrom, newTo));
3063
+ return { deco, node };
3058
3064
  }
3059
3065
  class CompositionWidget extends WidgetType {
3060
3066
  constructor(top, text, topView) {
@@ -3266,7 +3272,7 @@ function posAtCoords(view, coords, precise, bias = -1) {
3266
3272
  if (yOffset > docHeight)
3267
3273
  return view.state.doc.length;
3268
3274
  // Scan for a text block near the queried y position
3269
- for (let halfLine = view.defaultLineHeight / 2, bounced = false;;) {
3275
+ for (let halfLine = view.viewState.heightOracle.textHeight / 2, bounced = false;;) {
3270
3276
  block = view.elementAtHeight(yOffset);
3271
3277
  if (block.type == BlockType.Text)
3272
3278
  break;
@@ -3346,7 +3352,8 @@ function posAtCoords(view, coords, precise, bias = -1) {
3346
3352
  function posAtCoordsImprecise(view, contentRect, block, x, y) {
3347
3353
  let into = Math.round((x - contentRect.left) * view.defaultCharacterWidth);
3348
3354
  if (view.lineWrapping && block.height > view.defaultLineHeight * 1.5) {
3349
- let line = Math.floor((y - block.top) / view.defaultLineHeight);
3355
+ let textHeight = view.viewState.heightOracle.textHeight;
3356
+ let line = Math.floor((y - block.top - (view.defaultLineHeight - textHeight) * 0.5) / textHeight);
3350
3357
  into += line * view.viewState.heightOracle.lineLength;
3351
3358
  }
3352
3359
  let content = view.state.sliceDoc(block.from, block.to);
@@ -3457,7 +3464,7 @@ function moveVertically(view, start, forward, distance) {
3457
3464
  startY = (dir < 0 ? line.top : line.bottom) + docTop;
3458
3465
  }
3459
3466
  let resolvedGoal = rect.left + goal;
3460
- let dist = distance !== null && distance !== void 0 ? distance : (view.defaultLineHeight >> 1);
3467
+ let dist = distance !== null && distance !== void 0 ? distance : (view.viewState.heightOracle.textHeight >> 1);
3461
3468
  for (let extra = 0;; extra += 10) {
3462
3469
  let curY = startY + (dist + extra) * dir;
3463
3470
  let pos = posAtCoords(view, { x: resolvedGoal, y: curY }, false, dir);
@@ -3716,6 +3723,7 @@ class InputState {
3716
3723
  const PendingKeys = [
3717
3724
  { key: "Backspace", keyCode: 8, inputType: "deleteContentBackward" },
3718
3725
  { key: "Enter", keyCode: 13, inputType: "insertParagraph" },
3726
+ { key: "Enter", keyCode: 13, inputType: "insertLineBreak" },
3719
3727
  { key: "Delete", keyCode: 46, inputType: "deleteContentForward" }
3720
3728
  ];
3721
3729
  const EmacsyPendingKeys = "dthko";
@@ -3740,7 +3748,6 @@ class MouseSelection {
3740
3748
  doc.addEventListener("mouseup", this.up = this.up.bind(this));
3741
3749
  this.extend = startEvent.shiftKey;
3742
3750
  this.multiple = view.state.facet(EditorState.allowMultipleSelections) && addsSelectionRange(view, startEvent);
3743
- this.dragMove = dragMovesSelection(view, startEvent);
3744
3751
  this.dragging = isInPrimarySelection(view, startEvent) && getClickType(startEvent) == 1 ? null : false;
3745
3752
  }
3746
3753
  start(event) {
@@ -3961,7 +3968,7 @@ handlers.mousedown = (view, event) => {
3961
3968
  if (!style && event.button == 0)
3962
3969
  style = basicMouseSelection(view, event);
3963
3970
  if (style) {
3964
- let mustFocus = view.root.activeElement != view.contentDOM;
3971
+ let mustFocus = !view.hasFocus;
3965
3972
  view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
3966
3973
  if (mustFocus)
3967
3974
  view.observer.ignore(() => focusPreventScroll(view.contentDOM));
@@ -4078,7 +4085,7 @@ function dropText(view, event, text, direct) {
4078
4085
  let dropPos = view.posAtCoords({ x: event.clientX, y: event.clientY }, false);
4079
4086
  event.preventDefault();
4080
4087
  let { mouseSelection } = view.inputState;
4081
- let del = direct && mouseSelection && mouseSelection.dragging && mouseSelection.dragMove ?
4088
+ let del = direct && mouseSelection && mouseSelection.dragging && dragMovesSelection(view, event) ?
4082
4089
  { from: mouseSelection.dragging.from, to: mouseSelection.dragging.to } : null;
4083
4090
  let ins = { from: dropPos, insert: text };
4084
4091
  let changes = view.state.changes(del ? [del, ins] : ins);
@@ -5157,7 +5164,7 @@ class ViewState {
5157
5164
  let contentChanges = update.changedRanges;
5158
5165
  let heightChanges = ChangedRange.extendWithRanges(contentChanges, heightRelevantDecoChanges(prevDeco, this.stateDeco, update ? update.changes : ChangeSet.empty(this.state.doc.length)));
5159
5166
  let prevHeight = this.heightMap.height;
5160
- let scrollAnchor = this.scrolledToBottom ? null : this.lineBlockAtHeight(this.scrollTop);
5167
+ let scrollAnchor = this.scrolledToBottom ? null : this.scrollAnchorAt(this.scrollTop);
5161
5168
  this.heightMap = this.heightMap.applyChanges(this.stateDeco, update.startState.doc, this.heightOracle.setDoc(this.state.doc), heightChanges);
5162
5169
  if (this.heightMap.height != prevHeight)
5163
5170
  update.flags |= 2 /* Height */;
@@ -5217,7 +5224,7 @@ class ViewState {
5217
5224
  this.scrollAnchorHeight = -1;
5218
5225
  this.scrollTop = view.scrollDOM.scrollTop;
5219
5226
  }
5220
- this.scrolledToBottom = this.scrollTop > view.scrollDOM.scrollHeight - view.scrollDOM.clientHeight - 4;
5227
+ this.scrolledToBottom = isScrolledToBottom(view.scrollDOM);
5221
5228
  // Pixel viewport
5222
5229
  let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
5223
5230
  let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
@@ -5460,6 +5467,10 @@ class ViewState {
5460
5467
  lineBlockAtHeight(height) {
5461
5468
  return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
5462
5469
  }
5470
+ scrollAnchorAt(scrollTop) {
5471
+ let block = this.lineBlockAtHeight(scrollTop + 8);
5472
+ return block.from >= this.viewport.from || this.viewportLines[0].top - scrollTop > 200 ? block : this.viewportLines[0];
5473
+ }
5463
5474
  elementAtHeight(height) {
5464
5475
  return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.heightOracle, 0, 0), this.scaler);
5465
5476
  }
@@ -6832,22 +6843,23 @@ class EditorView {
6832
6843
  let updated = null;
6833
6844
  let sDOM = this.scrollDOM, { scrollTop } = sDOM;
6834
6845
  let { scrollAnchorPos, scrollAnchorHeight } = this.viewState;
6846
+ if (scrollTop != this.viewState.scrollTop)
6847
+ scrollAnchorHeight = -1;
6835
6848
  this.viewState.scrollAnchorHeight = -1;
6836
- if (scrollAnchorHeight < 0 || scrollTop != this.viewState.scrollTop) {
6837
- if (scrollTop > sDOM.scrollHeight - sDOM.clientHeight - 4) {
6838
- scrollAnchorPos = -1;
6839
- scrollAnchorHeight = this.viewState.heightMap.height;
6840
- }
6841
- else {
6842
- let block = this.viewState.lineBlockAtHeight(scrollTop);
6843
- scrollAnchorPos = block.from;
6844
- scrollAnchorHeight = block.top;
6845
- }
6846
- }
6847
6849
  try {
6848
6850
  for (let i = 0;; i++) {
6851
+ if (scrollAnchorHeight < 0) {
6852
+ if (isScrolledToBottom(sDOM)) {
6853
+ scrollAnchorPos = -1;
6854
+ scrollAnchorHeight = this.viewState.heightMap.height;
6855
+ }
6856
+ else {
6857
+ let block = this.viewState.scrollAnchorAt(scrollTop);
6858
+ scrollAnchorPos = block.from;
6859
+ scrollAnchorHeight = block.top;
6860
+ }
6861
+ }
6849
6862
  this.updateState = 1 /* Measuring */;
6850
- let oldViewport = this.viewport;
6851
6863
  let changed = this.viewState.measure(this);
6852
6864
  if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
6853
6865
  break;
@@ -6870,7 +6882,7 @@ class EditorView {
6870
6882
  return BadMeasure;
6871
6883
  }
6872
6884
  });
6873
- let update = ViewUpdate.create(this, this.state, []), redrawn = false, scrolled = false;
6885
+ let update = ViewUpdate.create(this, this.state, []), redrawn = false;
6874
6886
  update.flags |= changed;
6875
6887
  if (!updated)
6876
6888
  updated = update;
@@ -6894,28 +6906,28 @@ class EditorView {
6894
6906
  logException(this.state, e);
6895
6907
  }
6896
6908
  }
6897
- if (this.viewState.editorHeight) {
6898
- if (this.viewState.scrollTarget) {
6899
- this.docView.scrollIntoView(this.viewState.scrollTarget);
6900
- this.viewState.scrollTarget = null;
6901
- scrolled = true;
6902
- }
6903
- else if (scrollAnchorHeight > -1) {
6904
- let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
6905
- this.viewState.lineBlockAt(scrollAnchorPos).top;
6906
- let diff = newAnchorHeight - scrollAnchorHeight;
6907
- if (diff > 1 || diff < -1) {
6908
- sDOM.scrollTop = scrollTop + diff;
6909
- scrolled = true;
6910
- }
6911
- }
6912
- }
6913
6909
  if (redrawn)
6914
6910
  this.docView.updateSelection(true);
6915
- if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
6916
- !scrolled && this.measureRequests.length == 0)
6911
+ if (!update.viewportChanged && this.measureRequests.length == 0) {
6912
+ if (this.viewState.editorHeight) {
6913
+ if (this.viewState.scrollTarget) {
6914
+ this.docView.scrollIntoView(this.viewState.scrollTarget);
6915
+ this.viewState.scrollTarget = null;
6916
+ continue;
6917
+ }
6918
+ else {
6919
+ let newAnchorHeight = scrollAnchorPos < 0 ? this.viewState.heightMap.height :
6920
+ this.viewState.lineBlockAt(scrollAnchorPos).top;
6921
+ let diff = newAnchorHeight - scrollAnchorHeight;
6922
+ if (diff > 1 || diff < -1) {
6923
+ scrollTop = sDOM.scrollTop = scrollTop + diff;
6924
+ scrollAnchorHeight = -1;
6925
+ continue;
6926
+ }
6927
+ }
6928
+ }
6917
6929
  break;
6918
- scrollAnchorHeight = -1;
6930
+ }
6919
6931
  }
6920
6932
  }
6921
6933
  finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.13.2",
3
+ "version": "6.14.1",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",