@codemirror/view 0.19.32 → 0.19.33

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,19 @@
1
+ ## 0.19.33 (2021-12-16)
2
+
3
+ ### Breaking changes
4
+
5
+ `EditorView.scrollTo` and `EditorView.centerOn` are deprecated in favor of `EditorView.scrollIntoView`, and will be removed in the next breaking release.
6
+
7
+ ### Bug fixes
8
+
9
+ Fix an issue that could cause the editor to unnecessarily interfere with composition (especially visible on macOS Chrome).
10
+
11
+ A composition started with multiple lines selected will no longer be interruptd by the editor.
12
+
13
+ ### New features
14
+
15
+ The new `EditorView.scrollIntoView` function allows you to do more fine-grained scrolling.
16
+
1
17
  ## 0.19.32 (2021-12-15)
2
18
 
3
19
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -101,8 +101,7 @@ function windowRect(win) {
101
101
  return { left: 0, right: win.innerWidth,
102
102
  top: 0, bottom: win.innerHeight };
103
103
  }
104
- const ScrollSpace = 5;
105
- function scrollRectIntoView(dom, rect, side, center) {
104
+ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
106
105
  let doc = dom.ownerDocument, win = doc.defaultView;
107
106
  for (let cur = dom; cur;) {
108
107
  if (cur.nodeType == 1) { // Element
@@ -121,38 +120,42 @@ function scrollRectIntoView(dom, rect, side, center) {
121
120
  top: rect.top, bottom: rect.top + cur.clientHeight };
122
121
  }
123
122
  let moveX = 0, moveY = 0;
124
- if (center) {
123
+ if (y == "nearest") {
124
+ if (rect.top < bounding.top) {
125
+ moveY = -(bounding.top - rect.top + yMargin);
126
+ if (side > 0 && rect.bottom > bounding.bottom + moveY)
127
+ moveY = rect.bottom - bounding.bottom + moveY + yMargin;
128
+ }
129
+ else if (rect.bottom > bounding.bottom) {
130
+ moveY = rect.bottom - bounding.bottom + yMargin;
131
+ if (side < 0 && (rect.top - moveY) < bounding.top)
132
+ moveY = -(bounding.top + moveY - rect.top + yMargin);
133
+ }
134
+ }
135
+ else {
125
136
  let rectHeight = rect.bottom - rect.top, boundingHeight = bounding.bottom - bounding.top;
126
- let targetTop;
127
- if (rectHeight <= boundingHeight)
128
- targetTop = rect.top + rectHeight / 2 - boundingHeight / 2;
129
- else if (side < 0)
130
- targetTop = rect.top - ScrollSpace;
131
- else
132
- targetTop = rect.bottom + ScrollSpace - boundingHeight;
137
+ let targetTop = y == "center" && rectHeight <= boundingHeight ? rect.top + rectHeight / 2 - boundingHeight / 2 :
138
+ y == "start" || y == "center" && side < 0 ? rect.top - yMargin :
139
+ rect.bottom - boundingHeight + yMargin;
133
140
  moveY = targetTop - bounding.top;
134
- if (Math.abs(moveY) <= 1)
135
- moveY = 0;
136
- }
137
- else if (rect.top < bounding.top) {
138
- moveY = -(bounding.top - rect.top + ScrollSpace);
139
- if (side > 0 && rect.bottom > bounding.bottom + moveY)
140
- moveY = rect.bottom - bounding.bottom + moveY + ScrollSpace;
141
141
  }
142
- else if (rect.bottom > bounding.bottom) {
143
- moveY = rect.bottom - bounding.bottom + ScrollSpace;
144
- if (side < 0 && (rect.top - moveY) < bounding.top)
145
- moveY = -(bounding.top + moveY - rect.top + ScrollSpace);
146
- }
147
- if (rect.left < bounding.left) {
148
- moveX = -(bounding.left - rect.left + ScrollSpace);
149
- if (side > 0 && rect.right > bounding.right + moveX)
150
- moveX = rect.right - bounding.right + moveX + ScrollSpace;
142
+ if (x == "nearest") {
143
+ if (rect.left < bounding.left) {
144
+ moveX = -(bounding.left - rect.left + xMargin);
145
+ if (side > 0 && rect.right > bounding.right + moveX)
146
+ moveX = rect.right - bounding.right + moveX + xMargin;
147
+ }
148
+ else if (rect.right > bounding.right) {
149
+ moveX = rect.right - bounding.right + xMargin;
150
+ if (side < 0 && rect.left < bounding.left + moveX)
151
+ moveX = -(bounding.left + moveX - rect.left + xMargin);
152
+ }
151
153
  }
152
- else if (rect.right > bounding.right) {
153
- moveX = rect.right - bounding.right + ScrollSpace;
154
- if (side < 0 && rect.left < bounding.left + moveX)
155
- moveX = -(bounding.left + moveX - rect.left + ScrollSpace);
154
+ else {
155
+ let targetLeft = x == "center" ? rect.left + (rect.right - rect.left) / 2 - (bounding.right - bounding.left) / 2 :
156
+ (x == "start") == ltr ? rect.left - xMargin :
157
+ rect.right - (bounding.right - bounding.left) + xMargin;
158
+ moveX = targetLeft - bounding.left;
156
159
  }
157
160
  if (moveX || moveY) {
158
161
  if (top) {
@@ -176,7 +179,7 @@ function scrollRectIntoView(dom, rect, side, center) {
176
179
  if (top)
177
180
  break;
178
181
  cur = cur.assignedSlot || cur.parentNode;
179
- center = false;
182
+ x = y = "nearest";
180
183
  }
181
184
  else if (cur.nodeType == 11) { // A shadow root
182
185
  cur = cur.host;
@@ -263,6 +266,10 @@ function getRoot(node) {
263
266
  }
264
267
  return null;
265
268
  }
269
+ function clearAttributes(node) {
270
+ while (node.attributes.length)
271
+ node.removeAttributeNode(node.attributes[0]);
272
+ }
266
273
 
267
274
  class DOMPos {
268
275
  constructor(node, offset, precise = true) {
@@ -309,14 +316,16 @@ class ContentView {
309
316
  // given position.
310
317
  coordsAt(_pos, _side) { return null; }
311
318
  sync(track) {
312
- var _a;
313
319
  if (this.dirty & 2 /* Node */) {
314
320
  let parent = this.dom;
315
321
  let pos = parent.firstChild;
316
322
  for (let child of this.children) {
317
323
  if (child.dirty) {
318
- if (!child.dom && pos && !((_a = ContentView.get(pos)) === null || _a === void 0 ? void 0 : _a.parent))
319
- child.reuseDOM(pos);
324
+ if (!child.dom && pos) {
325
+ let contentView = ContentView.get(pos);
326
+ if (!contentView || !contentView.parent && contentView.constructor == child.constructor)
327
+ child.reuseDOM(pos);
328
+ }
320
329
  child.sync(track);
321
330
  child.dirty = 0 /* Not */;
322
331
  }
@@ -344,7 +353,7 @@ class ContentView {
344
353
  }
345
354
  }
346
355
  }
347
- reuseDOM(_dom) { return false; }
356
+ reuseDOM(_dom) { }
348
357
  localPosFromDOM(node, offset) {
349
358
  let after;
350
359
  if (node == this.dom) {
@@ -643,10 +652,8 @@ class TextView extends ContentView {
643
652
  }
644
653
  }
645
654
  reuseDOM(dom) {
646
- if (dom.nodeType != 3)
647
- return false;
648
- this.createDOM(dom);
649
- return true;
655
+ if (dom.nodeType == 3)
656
+ this.createDOM(dom);
650
657
  }
651
658
  merge(from, to, source) {
652
659
  if (source && (!(source instanceof TextView) || this.length - (to - from) + source.length > MaxJoinLen))
@@ -681,18 +688,26 @@ class MarkView extends ContentView {
681
688
  for (let ch of children)
682
689
  ch.setParent(this);
683
690
  }
684
- createDOM() {
685
- let dom = document.createElement(this.mark.tagName);
691
+ setAttrs(dom) {
692
+ clearAttributes(dom);
686
693
  if (this.mark.class)
687
694
  dom.className = this.mark.class;
688
695
  if (this.mark.attrs)
689
696
  for (let name in this.mark.attrs)
690
697
  dom.setAttribute(name, this.mark.attrs[name]);
691
- this.setDOM(dom);
698
+ return dom;
699
+ }
700
+ reuseDOM(node) {
701
+ if (node.nodeName == this.mark.tagName.toUpperCase()) {
702
+ this.setDOM(node);
703
+ this.dirty |= 4 /* Attrs */ | 2 /* Node */;
704
+ }
692
705
  }
693
706
  sync(track) {
694
- if (!this.dom || (this.dirty & 4 /* Attrs */))
695
- this.createDOM();
707
+ if (!this.dom)
708
+ this.setDOM(this.setAttrs(document.createElement(this.mark.tagName)));
709
+ else if (this.dirty & 4 /* Attrs */)
710
+ this.setAttrs(this.dom);
696
711
  super.sync(track);
697
712
  }
698
713
  merge(from, to, source, _hasStart, openStart, openEnd) {
@@ -836,8 +851,7 @@ class WidgetView extends ContentView {
836
851
  }
837
852
  class CompositionView extends WidgetView {
838
853
  domAtPos(pos) { return new DOMPos(this.widget.text, pos); }
839
- sync() { if (!this.dom)
840
- this.setDOM(this.widget.toDOM()); }
854
+ sync() { this.setDOM(this.widget.toDOM()); }
841
855
  localPosFromDOM(node, offset) {
842
856
  return !offset ? 0 : node.nodeType == 3 ? Math.min(offset, this.length) : this.length;
843
857
  }
@@ -1302,13 +1316,23 @@ class LineView extends ContentView {
1302
1316
  domAtPos(pos) {
1303
1317
  return inlineDOMAtPos(this.dom, this.children, pos);
1304
1318
  }
1319
+ reuseDOM(node) {
1320
+ if (node.nodeName == "DIV") {
1321
+ this.setDOM(node);
1322
+ this.dirty |= 4 /* Attrs */ | 2 /* Node */;
1323
+ }
1324
+ }
1305
1325
  sync(track) {
1306
1326
  var _a;
1307
- if (!this.dom || (this.dirty & 4 /* Attrs */)) {
1327
+ if (!this.dom) {
1308
1328
  this.setDOM(document.createElement("div"));
1309
1329
  this.dom.className = "cm-line";
1310
1330
  this.prevAttrs = this.attrs ? null : undefined;
1311
1331
  }
1332
+ else if (this.dirty & 4 /* Attrs */) {
1333
+ clearAttributes(this.dom);
1334
+ this.prevAttrs = this.attrs ? null : undefined;
1335
+ }
1312
1336
  if (this.prevAttrs !== undefined) {
1313
1337
  updateAttrs(this.dom, this.prevAttrs, this.attrs);
1314
1338
  this.dom.classList.add("cm-line");
@@ -1580,12 +1604,27 @@ const mouseSelectionStyle = state.Facet.define();
1580
1604
  const exceptionSink = state.Facet.define();
1581
1605
  const updateListener = state.Facet.define();
1582
1606
  const inputHandler = state.Facet.define();
1607
+ // FIXME remove
1583
1608
  const scrollTo = state.StateEffect.define({
1584
1609
  map: (range, changes) => range.map(changes)
1585
1610
  });
1611
+ // FIXME remove
1586
1612
  const centerOn = state.StateEffect.define({
1587
1613
  map: (range, changes) => range.map(changes)
1588
1614
  });
1615
+ class ScrollTarget {
1616
+ constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5) {
1617
+ this.range = range;
1618
+ this.y = y;
1619
+ this.x = x;
1620
+ this.yMargin = yMargin;
1621
+ this.xMargin = xMargin;
1622
+ }
1623
+ map(changes) {
1624
+ return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.y, this.x, this.yMargin, this.xMargin);
1625
+ }
1626
+ }
1627
+ const scrollIntoView = state.StateEffect.define({ map: (t, ch) => t.map(ch) });
1589
1628
  /**
1590
1629
  Log or report an unhandled exception in client code. Should
1591
1630
  probably only be used by extension code that allows client code to
@@ -2664,7 +2703,8 @@ class DocView extends ContentView {
2664
2703
  this.view.viewState.lineGapDeco
2665
2704
  ];
2666
2705
  }
2667
- scrollIntoView({ range, center }) {
2706
+ scrollIntoView(target) {
2707
+ let { range } = target;
2668
2708
  let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
2669
2709
  if (!rect)
2670
2710
  return;
@@ -2684,10 +2724,11 @@ class DocView extends ContentView {
2684
2724
  if (bottom != null)
2685
2725
  mBottom = Math.max(mBottom, bottom);
2686
2726
  }
2687
- scrollRectIntoView(this.view.scrollDOM, {
2727
+ let targetRect = {
2688
2728
  left: rect.left - mLeft, top: rect.top - mTop,
2689
2729
  right: rect.right + mRight, bottom: rect.bottom + mBottom
2690
- }, range.head < range.anchor ? -1 : 1, center);
2730
+ };
2731
+ scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, target.xMargin, target.yMargin, this.view.textDirection == exports.Direction.LTR);
2691
2732
  }
2692
2733
  }
2693
2734
  function betweenUneditable(pos) {
@@ -4521,15 +4562,6 @@ class LineGapWidget extends WidgetType {
4521
4562
  }
4522
4563
  get estimatedHeight() { return this.vertical ? this.size : -1; }
4523
4564
  }
4524
- class ScrollTarget {
4525
- constructor(range, center = false) {
4526
- this.range = range;
4527
- this.center = center;
4528
- }
4529
- map(changes) {
4530
- return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.center);
4531
- }
4532
- }
4533
4565
  class ViewState {
4534
4566
  constructor(state) {
4535
4567
  this.state = state;
@@ -4710,9 +4742,9 @@ class ViewState {
4710
4742
  let { head } = scrollTarget.range, viewHeight = this.editorHeight;
4711
4743
  if (head < viewport.from || head > viewport.to) {
4712
4744
  let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
4713
- if (scrollTarget.center)
4745
+ if (scrollTarget.y == "center")
4714
4746
  topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
4715
- else if (head < viewport.from)
4747
+ else if (scrollTarget.y == "start" || head < viewport.from)
4716
4748
  topPos = block.top;
4717
4749
  else
4718
4750
  topPos = block.bottom - viewHeight;
@@ -5818,7 +5850,9 @@ class EditorView {
5818
5850
  if (e.is(scrollTo))
5819
5851
  scrollTarget = new ScrollTarget(e.value);
5820
5852
  else if (e.is(centerOn))
5821
- scrollTarget = new ScrollTarget(e.value, true);
5853
+ scrollTarget = new ScrollTarget(e.value, "center");
5854
+ else if (e.is(scrollIntoView))
5855
+ scrollTarget = e.value;
5822
5856
  }
5823
5857
  }
5824
5858
  this.viewState.update(update, scrollTarget);
@@ -6386,6 +6420,14 @@ class EditorView {
6386
6420
  this.destroyed = true;
6387
6421
  }
6388
6422
  /**
6423
+ Returns an effect that can be
6424
+ [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a transaction to
6425
+ cause it to scroll the given position or range into view.
6426
+ */
6427
+ static scrollIntoView(pos, options = {}) {
6428
+ return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? state.EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin));
6429
+ }
6430
+ /**
6389
6431
  Facet that can be used to add DOM event handlers. The value
6390
6432
  should be an object mapping event names to handler functions. The
6391
6433
  first such function to return true will be assumed to have handled
@@ -6439,11 +6481,15 @@ class EditorView {
6439
6481
  /**
6440
6482
  Effect that can be [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a
6441
6483
  transaction to make it scroll the given range into view.
6484
+
6485
+ *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
6442
6486
  */
6443
6487
  EditorView.scrollTo = scrollTo;
6444
6488
  /**
6445
6489
  Effect that makes the editor scroll the given range to the
6446
6490
  center of the visible view.
6491
+
6492
+ *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
6447
6493
  */
6448
6494
  EditorView.centerOn = centerOn;
6449
6495
  /**
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import * as _codemirror_rangeset from '@codemirror/rangeset';
2
2
  import { RangeSet, RangeValue, Range } from '@codemirror/rangeset';
3
3
  export { Range } from '@codemirror/rangeset';
4
4
  import * as _codemirror_state from '@codemirror/state';
5
- import { EditorState, Extension, Transaction, ChangeSet, EditorSelection, TransactionSpec, SelectionRange, Facet } from '@codemirror/state';
5
+ import { EditorState, Extension, Transaction, ChangeSet, EditorSelection, TransactionSpec, SelectionRange, StateEffect, Facet } from '@codemirror/state';
6
6
  import { Line } from '@codemirror/text';
7
7
  import { StyleModule, StyleSpec } from 'style-mod';
8
8
 
@@ -264,6 +264,7 @@ interface Rect {
264
264
  readonly top: number;
265
265
  readonly bottom: number;
266
266
  }
267
+ declare type ScrollStrategy = "nearest" | "start" | "end" | "center";
267
268
 
268
269
  /**
269
270
  Command functions are used in key bindings and other types of user
@@ -1006,14 +1007,49 @@ declare class EditorView {
1006
1007
  /**
1007
1008
  Effect that can be [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a
1008
1009
  transaction to make it scroll the given range into view.
1010
+
1011
+ *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
1009
1012
  */
1010
1013
  static scrollTo: _codemirror_state.StateEffectType<SelectionRange>;
1011
1014
  /**
1012
1015
  Effect that makes the editor scroll the given range to the
1013
1016
  center of the visible view.
1017
+
1018
+ *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
1014
1019
  */
1015
1020
  static centerOn: _codemirror_state.StateEffectType<SelectionRange>;
1016
1021
  /**
1022
+ Returns an effect that can be
1023
+ [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a transaction to
1024
+ cause it to scroll the given position or range into view.
1025
+ */
1026
+ static scrollIntoView(pos: number | SelectionRange, options?: {
1027
+ /**
1028
+ By default (`"nearest"`) the position will be vertically
1029
+ scrolled only the minimal amount required to move the given
1030
+ position into view. You can set this to `"start"` to move it
1031
+ to the top of the view, `"end"` to move it to the bottom, or
1032
+ `"center"` to move it to the center.
1033
+ */
1034
+ y?: ScrollStrategy;
1035
+ /**
1036
+ Effect similar to
1037
+ [`y`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView^options.y), but for the
1038
+ horizontal scroll position.
1039
+ */
1040
+ x?: ScrollStrategy;
1041
+ /**
1042
+ Extra vertical distance to add when moving something into
1043
+ view. Not used with the `"center"` strategy. Defaults to 5.
1044
+ */
1045
+ yMargin?: number;
1046
+ /**
1047
+ Extra horizontal distance to add. Not used with the `"center"`
1048
+ strategy. Defaults to 5.
1049
+ */
1050
+ xMargin?: number;
1051
+ }): StateEffect<unknown>;
1052
+ /**
1017
1053
  Facet to add a [style
1018
1054
  module](https://github.com/marijnh/style-mod#documentation) to
1019
1055
  an editor view. The view will ensure that the module is
package/dist/index.js CHANGED
@@ -98,8 +98,7 @@ function windowRect(win) {
98
98
  return { left: 0, right: win.innerWidth,
99
99
  top: 0, bottom: win.innerHeight };
100
100
  }
101
- const ScrollSpace = 5;
102
- function scrollRectIntoView(dom, rect, side, center) {
101
+ function scrollRectIntoView(dom, rect, side, x, y, xMargin, yMargin, ltr) {
103
102
  let doc = dom.ownerDocument, win = doc.defaultView;
104
103
  for (let cur = dom; cur;) {
105
104
  if (cur.nodeType == 1) { // Element
@@ -118,38 +117,42 @@ function scrollRectIntoView(dom, rect, side, center) {
118
117
  top: rect.top, bottom: rect.top + cur.clientHeight };
119
118
  }
120
119
  let moveX = 0, moveY = 0;
121
- if (center) {
120
+ if (y == "nearest") {
121
+ if (rect.top < bounding.top) {
122
+ moveY = -(bounding.top - rect.top + yMargin);
123
+ if (side > 0 && rect.bottom > bounding.bottom + moveY)
124
+ moveY = rect.bottom - bounding.bottom + moveY + yMargin;
125
+ }
126
+ else if (rect.bottom > bounding.bottom) {
127
+ moveY = rect.bottom - bounding.bottom + yMargin;
128
+ if (side < 0 && (rect.top - moveY) < bounding.top)
129
+ moveY = -(bounding.top + moveY - rect.top + yMargin);
130
+ }
131
+ }
132
+ else {
122
133
  let rectHeight = rect.bottom - rect.top, boundingHeight = bounding.bottom - bounding.top;
123
- let targetTop;
124
- if (rectHeight <= boundingHeight)
125
- targetTop = rect.top + rectHeight / 2 - boundingHeight / 2;
126
- else if (side < 0)
127
- targetTop = rect.top - ScrollSpace;
128
- else
129
- targetTop = rect.bottom + ScrollSpace - boundingHeight;
134
+ let targetTop = y == "center" && rectHeight <= boundingHeight ? rect.top + rectHeight / 2 - boundingHeight / 2 :
135
+ y == "start" || y == "center" && side < 0 ? rect.top - yMargin :
136
+ rect.bottom - boundingHeight + yMargin;
130
137
  moveY = targetTop - bounding.top;
131
- if (Math.abs(moveY) <= 1)
132
- moveY = 0;
133
- }
134
- else if (rect.top < bounding.top) {
135
- moveY = -(bounding.top - rect.top + ScrollSpace);
136
- if (side > 0 && rect.bottom > bounding.bottom + moveY)
137
- moveY = rect.bottom - bounding.bottom + moveY + ScrollSpace;
138
138
  }
139
- else if (rect.bottom > bounding.bottom) {
140
- moveY = rect.bottom - bounding.bottom + ScrollSpace;
141
- if (side < 0 && (rect.top - moveY) < bounding.top)
142
- moveY = -(bounding.top + moveY - rect.top + ScrollSpace);
143
- }
144
- if (rect.left < bounding.left) {
145
- moveX = -(bounding.left - rect.left + ScrollSpace);
146
- if (side > 0 && rect.right > bounding.right + moveX)
147
- moveX = rect.right - bounding.right + moveX + ScrollSpace;
139
+ if (x == "nearest") {
140
+ if (rect.left < bounding.left) {
141
+ moveX = -(bounding.left - rect.left + xMargin);
142
+ if (side > 0 && rect.right > bounding.right + moveX)
143
+ moveX = rect.right - bounding.right + moveX + xMargin;
144
+ }
145
+ else if (rect.right > bounding.right) {
146
+ moveX = rect.right - bounding.right + xMargin;
147
+ if (side < 0 && rect.left < bounding.left + moveX)
148
+ moveX = -(bounding.left + moveX - rect.left + xMargin);
149
+ }
148
150
  }
149
- else if (rect.right > bounding.right) {
150
- moveX = rect.right - bounding.right + ScrollSpace;
151
- if (side < 0 && rect.left < bounding.left + moveX)
152
- moveX = -(bounding.left + moveX - rect.left + ScrollSpace);
151
+ else {
152
+ let targetLeft = x == "center" ? rect.left + (rect.right - rect.left) / 2 - (bounding.right - bounding.left) / 2 :
153
+ (x == "start") == ltr ? rect.left - xMargin :
154
+ rect.right - (bounding.right - bounding.left) + xMargin;
155
+ moveX = targetLeft - bounding.left;
153
156
  }
154
157
  if (moveX || moveY) {
155
158
  if (top) {
@@ -173,7 +176,7 @@ function scrollRectIntoView(dom, rect, side, center) {
173
176
  if (top)
174
177
  break;
175
178
  cur = cur.assignedSlot || cur.parentNode;
176
- center = false;
179
+ x = y = "nearest";
177
180
  }
178
181
  else if (cur.nodeType == 11) { // A shadow root
179
182
  cur = cur.host;
@@ -260,6 +263,10 @@ function getRoot(node) {
260
263
  }
261
264
  return null;
262
265
  }
266
+ function clearAttributes(node) {
267
+ while (node.attributes.length)
268
+ node.removeAttributeNode(node.attributes[0]);
269
+ }
263
270
 
264
271
  class DOMPos {
265
272
  constructor(node, offset, precise = true) {
@@ -306,14 +313,16 @@ class ContentView {
306
313
  // given position.
307
314
  coordsAt(_pos, _side) { return null; }
308
315
  sync(track) {
309
- var _a;
310
316
  if (this.dirty & 2 /* Node */) {
311
317
  let parent = this.dom;
312
318
  let pos = parent.firstChild;
313
319
  for (let child of this.children) {
314
320
  if (child.dirty) {
315
- if (!child.dom && pos && !((_a = ContentView.get(pos)) === null || _a === void 0 ? void 0 : _a.parent))
316
- child.reuseDOM(pos);
321
+ if (!child.dom && pos) {
322
+ let contentView = ContentView.get(pos);
323
+ if (!contentView || !contentView.parent && contentView.constructor == child.constructor)
324
+ child.reuseDOM(pos);
325
+ }
317
326
  child.sync(track);
318
327
  child.dirty = 0 /* Not */;
319
328
  }
@@ -341,7 +350,7 @@ class ContentView {
341
350
  }
342
351
  }
343
352
  }
344
- reuseDOM(_dom) { return false; }
353
+ reuseDOM(_dom) { }
345
354
  localPosFromDOM(node, offset) {
346
355
  let after;
347
356
  if (node == this.dom) {
@@ -640,10 +649,8 @@ class TextView extends ContentView {
640
649
  }
641
650
  }
642
651
  reuseDOM(dom) {
643
- if (dom.nodeType != 3)
644
- return false;
645
- this.createDOM(dom);
646
- return true;
652
+ if (dom.nodeType == 3)
653
+ this.createDOM(dom);
647
654
  }
648
655
  merge(from, to, source) {
649
656
  if (source && (!(source instanceof TextView) || this.length - (to - from) + source.length > MaxJoinLen))
@@ -678,18 +685,26 @@ class MarkView extends ContentView {
678
685
  for (let ch of children)
679
686
  ch.setParent(this);
680
687
  }
681
- createDOM() {
682
- let dom = document.createElement(this.mark.tagName);
688
+ setAttrs(dom) {
689
+ clearAttributes(dom);
683
690
  if (this.mark.class)
684
691
  dom.className = this.mark.class;
685
692
  if (this.mark.attrs)
686
693
  for (let name in this.mark.attrs)
687
694
  dom.setAttribute(name, this.mark.attrs[name]);
688
- this.setDOM(dom);
695
+ return dom;
696
+ }
697
+ reuseDOM(node) {
698
+ if (node.nodeName == this.mark.tagName.toUpperCase()) {
699
+ this.setDOM(node);
700
+ this.dirty |= 4 /* Attrs */ | 2 /* Node */;
701
+ }
689
702
  }
690
703
  sync(track) {
691
- if (!this.dom || (this.dirty & 4 /* Attrs */))
692
- this.createDOM();
704
+ if (!this.dom)
705
+ this.setDOM(this.setAttrs(document.createElement(this.mark.tagName)));
706
+ else if (this.dirty & 4 /* Attrs */)
707
+ this.setAttrs(this.dom);
693
708
  super.sync(track);
694
709
  }
695
710
  merge(from, to, source, _hasStart, openStart, openEnd) {
@@ -833,8 +848,7 @@ class WidgetView extends ContentView {
833
848
  }
834
849
  class CompositionView extends WidgetView {
835
850
  domAtPos(pos) { return new DOMPos(this.widget.text, pos); }
836
- sync() { if (!this.dom)
837
- this.setDOM(this.widget.toDOM()); }
851
+ sync() { this.setDOM(this.widget.toDOM()); }
838
852
  localPosFromDOM(node, offset) {
839
853
  return !offset ? 0 : node.nodeType == 3 ? Math.min(offset, this.length) : this.length;
840
854
  }
@@ -1298,13 +1312,23 @@ class LineView extends ContentView {
1298
1312
  domAtPos(pos) {
1299
1313
  return inlineDOMAtPos(this.dom, this.children, pos);
1300
1314
  }
1315
+ reuseDOM(node) {
1316
+ if (node.nodeName == "DIV") {
1317
+ this.setDOM(node);
1318
+ this.dirty |= 4 /* Attrs */ | 2 /* Node */;
1319
+ }
1320
+ }
1301
1321
  sync(track) {
1302
1322
  var _a;
1303
- if (!this.dom || (this.dirty & 4 /* Attrs */)) {
1323
+ if (!this.dom) {
1304
1324
  this.setDOM(document.createElement("div"));
1305
1325
  this.dom.className = "cm-line";
1306
1326
  this.prevAttrs = this.attrs ? null : undefined;
1307
1327
  }
1328
+ else if (this.dirty & 4 /* Attrs */) {
1329
+ clearAttributes(this.dom);
1330
+ this.prevAttrs = this.attrs ? null : undefined;
1331
+ }
1308
1332
  if (this.prevAttrs !== undefined) {
1309
1333
  updateAttrs(this.dom, this.prevAttrs, this.attrs);
1310
1334
  this.dom.classList.add("cm-line");
@@ -1576,12 +1600,27 @@ const mouseSelectionStyle = /*@__PURE__*/Facet.define();
1576
1600
  const exceptionSink = /*@__PURE__*/Facet.define();
1577
1601
  const updateListener = /*@__PURE__*/Facet.define();
1578
1602
  const inputHandler = /*@__PURE__*/Facet.define();
1603
+ // FIXME remove
1579
1604
  const scrollTo = /*@__PURE__*/StateEffect.define({
1580
1605
  map: (range, changes) => range.map(changes)
1581
1606
  });
1607
+ // FIXME remove
1582
1608
  const centerOn = /*@__PURE__*/StateEffect.define({
1583
1609
  map: (range, changes) => range.map(changes)
1584
1610
  });
1611
+ class ScrollTarget {
1612
+ constructor(range, y = "nearest", x = "nearest", yMargin = 5, xMargin = 5) {
1613
+ this.range = range;
1614
+ this.y = y;
1615
+ this.x = x;
1616
+ this.yMargin = yMargin;
1617
+ this.xMargin = xMargin;
1618
+ }
1619
+ map(changes) {
1620
+ return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.y, this.x, this.yMargin, this.xMargin);
1621
+ }
1622
+ }
1623
+ const scrollIntoView = /*@__PURE__*/StateEffect.define({ map: (t, ch) => t.map(ch) });
1585
1624
  /**
1586
1625
  Log or report an unhandled exception in client code. Should
1587
1626
  probably only be used by extension code that allows client code to
@@ -2659,7 +2698,8 @@ class DocView extends ContentView {
2659
2698
  this.view.viewState.lineGapDeco
2660
2699
  ];
2661
2700
  }
2662
- scrollIntoView({ range, center }) {
2701
+ scrollIntoView(target) {
2702
+ let { range } = target;
2663
2703
  let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
2664
2704
  if (!rect)
2665
2705
  return;
@@ -2679,10 +2719,11 @@ class DocView extends ContentView {
2679
2719
  if (bottom != null)
2680
2720
  mBottom = Math.max(mBottom, bottom);
2681
2721
  }
2682
- scrollRectIntoView(this.view.scrollDOM, {
2722
+ let targetRect = {
2683
2723
  left: rect.left - mLeft, top: rect.top - mTop,
2684
2724
  right: rect.right + mRight, bottom: rect.bottom + mBottom
2685
- }, range.head < range.anchor ? -1 : 1, center);
2725
+ };
2726
+ scrollRectIntoView(this.view.scrollDOM, targetRect, range.head < range.anchor ? -1 : 1, target.x, target.y, target.xMargin, target.yMargin, this.view.textDirection == Direction.LTR);
2686
2727
  }
2687
2728
  }
2688
2729
  function betweenUneditable(pos) {
@@ -4515,15 +4556,6 @@ class LineGapWidget extends WidgetType {
4515
4556
  }
4516
4557
  get estimatedHeight() { return this.vertical ? this.size : -1; }
4517
4558
  }
4518
- class ScrollTarget {
4519
- constructor(range, center = false) {
4520
- this.range = range;
4521
- this.center = center;
4522
- }
4523
- map(changes) {
4524
- return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.center);
4525
- }
4526
- }
4527
4559
  class ViewState {
4528
4560
  constructor(state) {
4529
4561
  this.state = state;
@@ -4704,9 +4736,9 @@ class ViewState {
4704
4736
  let { head } = scrollTarget.range, viewHeight = this.editorHeight;
4705
4737
  if (head < viewport.from || head > viewport.to) {
4706
4738
  let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
4707
- if (scrollTarget.center)
4739
+ if (scrollTarget.y == "center")
4708
4740
  topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
4709
- else if (head < viewport.from)
4741
+ else if (scrollTarget.y == "start" || head < viewport.from)
4710
4742
  topPos = block.top;
4711
4743
  else
4712
4744
  topPos = block.bottom - viewHeight;
@@ -5812,7 +5844,9 @@ class EditorView {
5812
5844
  if (e.is(scrollTo))
5813
5845
  scrollTarget = new ScrollTarget(e.value);
5814
5846
  else if (e.is(centerOn))
5815
- scrollTarget = new ScrollTarget(e.value, true);
5847
+ scrollTarget = new ScrollTarget(e.value, "center");
5848
+ else if (e.is(scrollIntoView))
5849
+ scrollTarget = e.value;
5816
5850
  }
5817
5851
  }
5818
5852
  this.viewState.update(update, scrollTarget);
@@ -6380,6 +6414,14 @@ class EditorView {
6380
6414
  this.destroyed = true;
6381
6415
  }
6382
6416
  /**
6417
+ Returns an effect that can be
6418
+ [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a transaction to
6419
+ cause it to scroll the given position or range into view.
6420
+ */
6421
+ static scrollIntoView(pos, options = {}) {
6422
+ return scrollIntoView.of(new ScrollTarget(typeof pos == "number" ? EditorSelection.cursor(pos) : pos, options.y, options.x, options.yMargin, options.xMargin));
6423
+ }
6424
+ /**
6383
6425
  Facet that can be used to add DOM event handlers. The value
6384
6426
  should be an object mapping event names to handler functions. The
6385
6427
  first such function to return true will be assumed to have handled
@@ -6433,11 +6475,15 @@ class EditorView {
6433
6475
  /**
6434
6476
  Effect that can be [added](https://codemirror.net/6/docs/ref/#state.TransactionSpec.effects) to a
6435
6477
  transaction to make it scroll the given range into view.
6478
+
6479
+ *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
6436
6480
  */
6437
6481
  EditorView.scrollTo = scrollTo;
6438
6482
  /**
6439
6483
  Effect that makes the editor scroll the given range to the
6440
6484
  center of the visible view.
6485
+
6486
+ *Deprecated*. Use [`scrollIntoView`](https://codemirror.net/6/docs/ref/#view.EditorView^scrollIntoView) instead.
6441
6487
  */
6442
6488
  EditorView.centerOn = centerOn;
6443
6489
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "0.19.32",
3
+ "version": "0.19.33",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",