@codemirror/view 0.19.10 → 0.19.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 0.19.11 (2021-11-03)
2
+
3
+ ### Breaking changes
4
+
5
+ `EditorView.scrollPosIntoView` has been deprecated. Use the `EditorView.scrollTo` effect instead.
6
+
7
+ ### New features
8
+
9
+ The new `EditorView.centerOn` effect can be used to scroll a given range to the center of the view.
10
+
1
11
  ## 0.19.10 (2021-11-02)
2
12
 
3
13
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -102,9 +102,9 @@ function windowRect(win) {
102
102
  top: 0, bottom: win.innerHeight };
103
103
  }
104
104
  const ScrollSpace = 5;
105
- function scrollRectIntoView(dom, rect, side) {
105
+ function scrollRectIntoView(dom, rect, side, center) {
106
106
  let doc = dom.ownerDocument, win = doc.defaultView;
107
- for (let cur = dom.parentNode; cur;) {
107
+ for (let cur = dom; cur;) {
108
108
  if (cur.nodeType == 1) { // Element
109
109
  let bounding, top = cur == doc.body;
110
110
  if (top) {
@@ -121,7 +121,20 @@ function scrollRectIntoView(dom, rect, side) {
121
121
  top: rect.top, bottom: rect.top + cur.clientHeight };
122
122
  }
123
123
  let moveX = 0, moveY = 0;
124
- if (rect.top < bounding.top) {
124
+ if (center) {
125
+ 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;
133
+ moveY = targetTop - bounding.top;
134
+ if (Math.abs(moveY) <= 1)
135
+ moveY = 0;
136
+ }
137
+ else if (rect.top < bounding.top) {
125
138
  moveY = -(bounding.top - rect.top + ScrollSpace);
126
139
  if (side > 0 && rect.bottom > bounding.bottom + moveY)
127
140
  moveY = rect.bottom - bounding.bottom + moveY + ScrollSpace;
@@ -163,6 +176,7 @@ function scrollRectIntoView(dom, rect, side) {
163
176
  if (top)
164
177
  break;
165
178
  cur = cur.assignedSlot || cur.parentNode;
179
+ center = false;
166
180
  }
167
181
  else if (cur.nodeType == 11) { // A shadow root
168
182
  cur = cur.host;
@@ -1560,6 +1574,9 @@ const inputHandler = state.Facet.define();
1560
1574
  const scrollTo = state.StateEffect.define({
1561
1575
  map: (range, changes) => range.map(changes)
1562
1576
  });
1577
+ const centerOn = state.StateEffect.define({
1578
+ map: (range, changes) => range.map(changes)
1579
+ });
1563
1580
  /**
1564
1581
  Log or report an unhandled exception in client code. Should
1565
1582
  probably only be used by extension code that allows client code to
@@ -2304,7 +2321,7 @@ class DocView extends ContentView {
2304
2321
  this.view.viewState.lineGapDeco
2305
2322
  ];
2306
2323
  }
2307
- scrollRangeIntoView(range) {
2324
+ scrollIntoView({ range, center }) {
2308
2325
  let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
2309
2326
  if (!rect)
2310
2327
  return;
@@ -2324,10 +2341,10 @@ class DocView extends ContentView {
2324
2341
  if (bottom != null)
2325
2342
  mBottom = Math.max(mBottom, bottom);
2326
2343
  }
2327
- scrollRectIntoView(this.dom, {
2344
+ scrollRectIntoView(this.view.scrollDOM, {
2328
2345
  left: rect.left - mLeft, top: rect.top - mTop,
2329
2346
  right: rect.right + mRight, bottom: rect.bottom + mBottom
2330
- }, range.head < range.anchor ? -1 : 1);
2347
+ }, range.head < range.anchor ? -1 : 1, center);
2331
2348
  }
2332
2349
  }
2333
2350
  function betweenUneditable(pos) {
@@ -4446,6 +4463,15 @@ class LineGapWidget extends WidgetType {
4446
4463
  }
4447
4464
  get estimatedHeight() { return this.vertical ? this.size : -1; }
4448
4465
  }
4466
+ class ScrollTarget {
4467
+ constructor(range, center = false) {
4468
+ this.range = range;
4469
+ this.center = center;
4470
+ }
4471
+ map(changes) {
4472
+ return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.center);
4473
+ }
4474
+ }
4449
4475
  class ViewState {
4450
4476
  constructor(state) {
4451
4477
  this.state = state;
@@ -4458,7 +4484,7 @@ class ViewState {
4458
4484
  this.heightOracle = new HeightOracle;
4459
4485
  // See VP.MaxDOMHeight
4460
4486
  this.scaler = IdScaler;
4461
- this.scrollTo = null;
4487
+ this.scrollTarget = null;
4462
4488
  // Briefly set to true when printing, to disable viewport limiting
4463
4489
  this.printing = false;
4464
4490
  this.visibleRanges = [];
@@ -4491,7 +4517,7 @@ class ViewState {
4491
4517
  this.scaler = this.heightMap.height <= 7000000 /* MaxDOMHeight */ ? IdScaler :
4492
4518
  new BigScaler(this.heightOracle.doc, this.heightMap, this.viewports);
4493
4519
  }
4494
- update(update, scrollTo = null) {
4520
+ update(update, scrollTarget = null) {
4495
4521
  let prev = this.state;
4496
4522
  this.state = update.state;
4497
4523
  let newDeco = this.state.facet(decorations);
@@ -4502,15 +4528,16 @@ class ViewState {
4502
4528
  if (this.heightMap.height != prevHeight)
4503
4529
  update.flags |= 2 /* Height */;
4504
4530
  let viewport = heightChanges.length ? this.mapViewport(this.viewport, update.changes) : this.viewport;
4505
- if (scrollTo && (scrollTo.head < viewport.from || scrollTo.head > viewport.to) || !this.viewportIsAppropriate(viewport))
4506
- viewport = this.getViewport(0, scrollTo);
4531
+ if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
4532
+ !this.viewportIsAppropriate(viewport))
4533
+ viewport = this.getViewport(0, scrollTarget);
4507
4534
  this.viewport = viewport;
4508
4535
  this.updateForViewport();
4509
4536
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > 15000 /* MinViewPort */)
4510
4537
  this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
4511
4538
  update.flags |= this.computeVisibleRanges();
4512
- if (scrollTo)
4513
- this.scrollTo = scrollTo;
4539
+ if (scrollTarget)
4540
+ this.scrollTarget = scrollTarget;
4514
4541
  if (!this.mustEnforceCursorAssoc && update.selectionSet && update.view.lineWrapping &&
4515
4542
  update.state.selection.main.empty && update.state.selection.main.assoc)
4516
4543
  this.mustEnforceCursorAssoc = true;
@@ -4563,8 +4590,8 @@ class ViewState {
4563
4590
  if (oracle.heightChanged)
4564
4591
  result |= 2 /* Height */;
4565
4592
  if (!this.viewportIsAppropriate(this.viewport, bias) ||
4566
- this.scrollTo && (this.scrollTo.head < this.viewport.from || this.scrollTo.head > this.viewport.to))
4567
- this.viewport = this.getViewport(bias, this.scrollTo);
4593
+ this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to))
4594
+ this.viewport = this.getViewport(bias, this.scrollTarget);
4568
4595
  this.updateForViewport();
4569
4596
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > 15000 /* MinViewPort */)
4570
4597
  this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
@@ -4581,22 +4608,25 @@ class ViewState {
4581
4608
  }
4582
4609
  get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top, 0); }
4583
4610
  get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom, 0); }
4584
- getViewport(bias, scrollTo) {
4611
+ getViewport(bias, scrollTarget) {
4585
4612
  // This will divide VP.Margin between the top and the
4586
4613
  // bottom, depending on the bias (the change in viewport position
4587
4614
  // since the last update). It'll hold a number between 0 and 1
4588
4615
  let marginTop = 0.5 - Math.max(-0.5, Math.min(0.5, bias / 1000 /* Margin */ / 2));
4589
4616
  let map = this.heightMap, doc = this.state.doc, { visibleTop, visibleBottom } = this;
4590
4617
  let viewport = new Viewport(map.lineAt(visibleTop - marginTop * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(visibleBottom + (1 - marginTop) * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).to);
4591
- // If scrollTo is given, make sure the viewport includes that position
4592
- if (scrollTo) {
4593
- if (scrollTo.head < viewport.from) {
4594
- let { top: newTop } = map.lineAt(scrollTo.head, QueryType.ByPos, doc, 0, 0);
4595
- viewport = new Viewport(map.lineAt(newTop - 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(newTop + (visibleBottom - visibleTop) + 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).to);
4596
- }
4597
- else if (scrollTo.head > viewport.to) {
4598
- let { bottom: newBottom } = map.lineAt(scrollTo.head, QueryType.ByPos, doc, 0, 0);
4599
- viewport = new Viewport(map.lineAt(newBottom - (visibleBottom - visibleTop) - 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(newBottom + 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).to);
4618
+ // If scrollTarget is given, make sure the viewport includes that position
4619
+ if (scrollTarget) {
4620
+ let { head } = scrollTarget.range, viewHeight = visibleBottom - visibleTop;
4621
+ if (head < viewport.from || head > viewport.to) {
4622
+ let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
4623
+ if (scrollTarget.center)
4624
+ topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
4625
+ else if (head < viewport.from)
4626
+ topPos = block.top;
4627
+ else
4628
+ topPos = block.bottom - viewHeight;
4629
+ viewport = new Viewport(map.lineAt(topPos - 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(topPos + viewHeight + 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).to);
4600
4630
  }
4601
4631
  }
4602
4632
  return viewport;
@@ -5691,21 +5721,24 @@ class EditorView {
5691
5721
  if (state$1.facet(state.EditorState.phrases) != this.state.facet(state.EditorState.phrases))
5692
5722
  return this.setState(state$1);
5693
5723
  update = new ViewUpdate(this, state$1, transactions);
5694
- let scrollPos = null;
5724
+ let scrollTarget = null;
5695
5725
  try {
5696
5726
  this.updateState = 2 /* Updating */;
5697
5727
  for (let tr of transactions) {
5698
- if (scrollPos)
5699
- scrollPos = scrollPos.map(tr.changes);
5728
+ if (scrollTarget)
5729
+ scrollTarget = scrollTarget.map(tr.changes);
5700
5730
  if (tr.scrollIntoView) {
5701
5731
  let { main } = tr.state.selection;
5702
- scrollPos = main.empty ? main : state.EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1);
5732
+ scrollTarget = new ScrollTarget(main.empty ? main : state.EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1));
5703
5733
  }
5704
- for (let e of tr.effects)
5734
+ for (let e of tr.effects) {
5705
5735
  if (e.is(scrollTo))
5706
- scrollPos = e.value;
5736
+ scrollTarget = new ScrollTarget(e.value);
5737
+ else if (e.is(centerOn))
5738
+ scrollTarget = new ScrollTarget(e.value, true);
5739
+ }
5707
5740
  }
5708
- this.viewState.update(update, scrollPos);
5741
+ this.viewState.update(update, scrollTarget);
5709
5742
  this.bidiCache = CachedOrder.update(this.bidiCache, update.changes);
5710
5743
  if (!update.empty) {
5711
5744
  this.updatePlugins(update);
@@ -5720,7 +5753,7 @@ class EditorView {
5720
5753
  finally {
5721
5754
  this.updateState = 0 /* Idle */;
5722
5755
  }
5723
- if (redrawn || scrollPos || this.viewState.mustEnforceCursorAssoc)
5756
+ if (redrawn || scrollTarget || this.viewState.mustEnforceCursorAssoc)
5724
5757
  this.requestMeasure();
5725
5758
  if (!update.empty)
5726
5759
  for (let listener of this.state.facet(updateListener))
@@ -5802,7 +5835,7 @@ class EditorView {
5802
5835
  this.updateState = 1 /* Measuring */;
5803
5836
  let oldViewport = this.viewport;
5804
5837
  let changed = this.viewState.measure(this.docView, i > 0);
5805
- if (!changed && !this.measureRequests.length && this.viewState.scrollTo == null)
5838
+ if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
5806
5839
  break;
5807
5840
  if (i > 5) {
5808
5841
  console.warn("Viewport failed to stabilize");
@@ -5844,9 +5877,9 @@ class EditorView {
5844
5877
  logException(this.state, e);
5845
5878
  }
5846
5879
  }
5847
- if (this.viewState.scrollTo) {
5848
- this.docView.scrollRangeIntoView(this.viewState.scrollTo);
5849
- this.viewState.scrollTo = null;
5880
+ if (this.viewState.scrollTarget) {
5881
+ this.docView.scrollIntoView(this.viewState.scrollTarget);
5882
+ this.viewState.scrollTarget = null;
5850
5883
  }
5851
5884
  if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to && this.measureRequests.length == 0)
5852
5885
  break;
@@ -6069,12 +6102,9 @@ class EditorView {
6069
6102
  moveVertically(start, forward, distance) {
6070
6103
  return skipAtoms(this, start, moveVertically(this, start, forward, distance));
6071
6104
  }
6072
- /**
6073
- Scroll the given document position into view.
6074
- */
6105
+ // FIXME remove on next major version
6075
6106
  scrollPosIntoView(pos) {
6076
- this.viewState.scrollTo = state.EditorSelection.cursor(pos);
6077
- this.requestMeasure();
6107
+ this.dispatch({ effects: scrollTo.of(state.EditorSelection.cursor(pos)) });
6078
6108
  }
6079
6109
  /**
6080
6110
  Find the DOM parent node and offset (child offset if `node` is
@@ -6250,6 +6280,11 @@ transaction to make it scroll the given range into view.
6250
6280
  */
6251
6281
  EditorView.scrollTo = scrollTo;
6252
6282
  /**
6283
+ Effect that makes the editor scroll the given range to the
6284
+ center of the visible view.
6285
+ */
6286
+ EditorView.centerOn = centerOn;
6287
+ /**
6253
6288
  Facet to add a [style
6254
6289
  module](https://github.com/marijnh/style-mod#documentation) to
6255
6290
  an editor view. The view will ensure that the module is
package/dist/index.d.ts CHANGED
@@ -856,9 +856,6 @@ declare class EditorView {
856
856
  used.
857
857
  */
858
858
  moveVertically(start: SelectionRange, forward: boolean, distance?: number): SelectionRange;
859
- /**
860
- Scroll the given document position into view.
861
- */
862
859
  scrollPosIntoView(pos: number): void;
863
860
  /**
864
861
  Find the DOM parent node and offset (child offset if `node` is
@@ -949,6 +946,11 @@ declare class EditorView {
949
946
  */
950
947
  static scrollTo: _codemirror_state.StateEffectType<SelectionRange>;
951
948
  /**
949
+ Effect that makes the editor scroll the given range to the
950
+ center of the visible view.
951
+ */
952
+ static centerOn: _codemirror_state.StateEffectType<SelectionRange>;
953
+ /**
952
954
  Facet to add a [style
953
955
  module](https://github.com/marijnh/style-mod#documentation) to
954
956
  an editor view. The view will ensure that the module is
package/dist/index.js CHANGED
@@ -99,9 +99,9 @@ function windowRect(win) {
99
99
  top: 0, bottom: win.innerHeight };
100
100
  }
101
101
  const ScrollSpace = 5;
102
- function scrollRectIntoView(dom, rect, side) {
102
+ function scrollRectIntoView(dom, rect, side, center) {
103
103
  let doc = dom.ownerDocument, win = doc.defaultView;
104
- for (let cur = dom.parentNode; cur;) {
104
+ for (let cur = dom; cur;) {
105
105
  if (cur.nodeType == 1) { // Element
106
106
  let bounding, top = cur == doc.body;
107
107
  if (top) {
@@ -118,7 +118,20 @@ function scrollRectIntoView(dom, rect, side) {
118
118
  top: rect.top, bottom: rect.top + cur.clientHeight };
119
119
  }
120
120
  let moveX = 0, moveY = 0;
121
- if (rect.top < bounding.top) {
121
+ if (center) {
122
+ 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;
130
+ moveY = targetTop - bounding.top;
131
+ if (Math.abs(moveY) <= 1)
132
+ moveY = 0;
133
+ }
134
+ else if (rect.top < bounding.top) {
122
135
  moveY = -(bounding.top - rect.top + ScrollSpace);
123
136
  if (side > 0 && rect.bottom > bounding.bottom + moveY)
124
137
  moveY = rect.bottom - bounding.bottom + moveY + ScrollSpace;
@@ -160,6 +173,7 @@ function scrollRectIntoView(dom, rect, side) {
160
173
  if (top)
161
174
  break;
162
175
  cur = cur.assignedSlot || cur.parentNode;
176
+ center = false;
163
177
  }
164
178
  else if (cur.nodeType == 11) { // A shadow root
165
179
  cur = cur.host;
@@ -1556,6 +1570,9 @@ const inputHandler = /*@__PURE__*/Facet.define();
1556
1570
  const scrollTo = /*@__PURE__*/StateEffect.define({
1557
1571
  map: (range, changes) => range.map(changes)
1558
1572
  });
1573
+ const centerOn = /*@__PURE__*/StateEffect.define({
1574
+ map: (range, changes) => range.map(changes)
1575
+ });
1559
1576
  /**
1560
1577
  Log or report an unhandled exception in client code. Should
1561
1578
  probably only be used by extension code that allows client code to
@@ -2300,7 +2317,7 @@ class DocView extends ContentView {
2300
2317
  this.view.viewState.lineGapDeco
2301
2318
  ];
2302
2319
  }
2303
- scrollRangeIntoView(range) {
2320
+ scrollIntoView({ range, center }) {
2304
2321
  let rect = this.coordsAt(range.head, range.empty ? range.assoc : range.head > range.anchor ? -1 : 1), other;
2305
2322
  if (!rect)
2306
2323
  return;
@@ -2320,10 +2337,10 @@ class DocView extends ContentView {
2320
2337
  if (bottom != null)
2321
2338
  mBottom = Math.max(mBottom, bottom);
2322
2339
  }
2323
- scrollRectIntoView(this.dom, {
2340
+ scrollRectIntoView(this.view.scrollDOM, {
2324
2341
  left: rect.left - mLeft, top: rect.top - mTop,
2325
2342
  right: rect.right + mRight, bottom: rect.bottom + mBottom
2326
- }, range.head < range.anchor ? -1 : 1);
2343
+ }, range.head < range.anchor ? -1 : 1, center);
2327
2344
  }
2328
2345
  }
2329
2346
  function betweenUneditable(pos) {
@@ -4440,6 +4457,15 @@ class LineGapWidget extends WidgetType {
4440
4457
  }
4441
4458
  get estimatedHeight() { return this.vertical ? this.size : -1; }
4442
4459
  }
4460
+ class ScrollTarget {
4461
+ constructor(range, center = false) {
4462
+ this.range = range;
4463
+ this.center = center;
4464
+ }
4465
+ map(changes) {
4466
+ return changes.empty ? this : new ScrollTarget(this.range.map(changes), this.center);
4467
+ }
4468
+ }
4443
4469
  class ViewState {
4444
4470
  constructor(state) {
4445
4471
  this.state = state;
@@ -4452,7 +4478,7 @@ class ViewState {
4452
4478
  this.heightOracle = new HeightOracle;
4453
4479
  // See VP.MaxDOMHeight
4454
4480
  this.scaler = IdScaler;
4455
- this.scrollTo = null;
4481
+ this.scrollTarget = null;
4456
4482
  // Briefly set to true when printing, to disable viewport limiting
4457
4483
  this.printing = false;
4458
4484
  this.visibleRanges = [];
@@ -4485,7 +4511,7 @@ class ViewState {
4485
4511
  this.scaler = this.heightMap.height <= 7000000 /* MaxDOMHeight */ ? IdScaler :
4486
4512
  new BigScaler(this.heightOracle.doc, this.heightMap, this.viewports);
4487
4513
  }
4488
- update(update, scrollTo = null) {
4514
+ update(update, scrollTarget = null) {
4489
4515
  let prev = this.state;
4490
4516
  this.state = update.state;
4491
4517
  let newDeco = this.state.facet(decorations);
@@ -4496,15 +4522,16 @@ class ViewState {
4496
4522
  if (this.heightMap.height != prevHeight)
4497
4523
  update.flags |= 2 /* Height */;
4498
4524
  let viewport = heightChanges.length ? this.mapViewport(this.viewport, update.changes) : this.viewport;
4499
- if (scrollTo && (scrollTo.head < viewport.from || scrollTo.head > viewport.to) || !this.viewportIsAppropriate(viewport))
4500
- viewport = this.getViewport(0, scrollTo);
4525
+ if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
4526
+ !this.viewportIsAppropriate(viewport))
4527
+ viewport = this.getViewport(0, scrollTarget);
4501
4528
  this.viewport = viewport;
4502
4529
  this.updateForViewport();
4503
4530
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > 15000 /* MinViewPort */)
4504
4531
  this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
4505
4532
  update.flags |= this.computeVisibleRanges();
4506
- if (scrollTo)
4507
- this.scrollTo = scrollTo;
4533
+ if (scrollTarget)
4534
+ this.scrollTarget = scrollTarget;
4508
4535
  if (!this.mustEnforceCursorAssoc && update.selectionSet && update.view.lineWrapping &&
4509
4536
  update.state.selection.main.empty && update.state.selection.main.assoc)
4510
4537
  this.mustEnforceCursorAssoc = true;
@@ -4557,8 +4584,8 @@ class ViewState {
4557
4584
  if (oracle.heightChanged)
4558
4585
  result |= 2 /* Height */;
4559
4586
  if (!this.viewportIsAppropriate(this.viewport, bias) ||
4560
- this.scrollTo && (this.scrollTo.head < this.viewport.from || this.scrollTo.head > this.viewport.to))
4561
- this.viewport = this.getViewport(bias, this.scrollTo);
4587
+ this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to))
4588
+ this.viewport = this.getViewport(bias, this.scrollTarget);
4562
4589
  this.updateForViewport();
4563
4590
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > 15000 /* MinViewPort */)
4564
4591
  this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
@@ -4575,22 +4602,25 @@ class ViewState {
4575
4602
  }
4576
4603
  get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top, 0); }
4577
4604
  get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom, 0); }
4578
- getViewport(bias, scrollTo) {
4605
+ getViewport(bias, scrollTarget) {
4579
4606
  // This will divide VP.Margin between the top and the
4580
4607
  // bottom, depending on the bias (the change in viewport position
4581
4608
  // since the last update). It'll hold a number between 0 and 1
4582
4609
  let marginTop = 0.5 - Math.max(-0.5, Math.min(0.5, bias / 1000 /* Margin */ / 2));
4583
4610
  let map = this.heightMap, doc = this.state.doc, { visibleTop, visibleBottom } = this;
4584
4611
  let viewport = new Viewport(map.lineAt(visibleTop - marginTop * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(visibleBottom + (1 - marginTop) * 1000 /* Margin */, QueryType.ByHeight, doc, 0, 0).to);
4585
- // If scrollTo is given, make sure the viewport includes that position
4586
- if (scrollTo) {
4587
- if (scrollTo.head < viewport.from) {
4588
- let { top: newTop } = map.lineAt(scrollTo.head, QueryType.ByPos, doc, 0, 0);
4589
- viewport = new Viewport(map.lineAt(newTop - 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(newTop + (visibleBottom - visibleTop) + 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).to);
4590
- }
4591
- else if (scrollTo.head > viewport.to) {
4592
- let { bottom: newBottom } = map.lineAt(scrollTo.head, QueryType.ByPos, doc, 0, 0);
4593
- viewport = new Viewport(map.lineAt(newBottom - (visibleBottom - visibleTop) - 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(newBottom + 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).to);
4612
+ // If scrollTarget is given, make sure the viewport includes that position
4613
+ if (scrollTarget) {
4614
+ let { head } = scrollTarget.range, viewHeight = visibleBottom - visibleTop;
4615
+ if (head < viewport.from || head > viewport.to) {
4616
+ let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
4617
+ if (scrollTarget.center)
4618
+ topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
4619
+ else if (head < viewport.from)
4620
+ topPos = block.top;
4621
+ else
4622
+ topPos = block.bottom - viewHeight;
4623
+ viewport = new Viewport(map.lineAt(topPos - 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(topPos + viewHeight + 1000 /* Margin */ / 2, QueryType.ByHeight, doc, 0, 0).to);
4594
4624
  }
4595
4625
  }
4596
4626
  return viewport;
@@ -5685,21 +5715,24 @@ class EditorView {
5685
5715
  if (state.facet(EditorState.phrases) != this.state.facet(EditorState.phrases))
5686
5716
  return this.setState(state);
5687
5717
  update = new ViewUpdate(this, state, transactions);
5688
- let scrollPos = null;
5718
+ let scrollTarget = null;
5689
5719
  try {
5690
5720
  this.updateState = 2 /* Updating */;
5691
5721
  for (let tr of transactions) {
5692
- if (scrollPos)
5693
- scrollPos = scrollPos.map(tr.changes);
5722
+ if (scrollTarget)
5723
+ scrollTarget = scrollTarget.map(tr.changes);
5694
5724
  if (tr.scrollIntoView) {
5695
5725
  let { main } = tr.state.selection;
5696
- scrollPos = main.empty ? main : EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1);
5726
+ scrollTarget = new ScrollTarget(main.empty ? main : EditorSelection.cursor(main.head, main.head > main.anchor ? -1 : 1));
5697
5727
  }
5698
- for (let e of tr.effects)
5728
+ for (let e of tr.effects) {
5699
5729
  if (e.is(scrollTo))
5700
- scrollPos = e.value;
5730
+ scrollTarget = new ScrollTarget(e.value);
5731
+ else if (e.is(centerOn))
5732
+ scrollTarget = new ScrollTarget(e.value, true);
5733
+ }
5701
5734
  }
5702
- this.viewState.update(update, scrollPos);
5735
+ this.viewState.update(update, scrollTarget);
5703
5736
  this.bidiCache = CachedOrder.update(this.bidiCache, update.changes);
5704
5737
  if (!update.empty) {
5705
5738
  this.updatePlugins(update);
@@ -5714,7 +5747,7 @@ class EditorView {
5714
5747
  finally {
5715
5748
  this.updateState = 0 /* Idle */;
5716
5749
  }
5717
- if (redrawn || scrollPos || this.viewState.mustEnforceCursorAssoc)
5750
+ if (redrawn || scrollTarget || this.viewState.mustEnforceCursorAssoc)
5718
5751
  this.requestMeasure();
5719
5752
  if (!update.empty)
5720
5753
  for (let listener of this.state.facet(updateListener))
@@ -5796,7 +5829,7 @@ class EditorView {
5796
5829
  this.updateState = 1 /* Measuring */;
5797
5830
  let oldViewport = this.viewport;
5798
5831
  let changed = this.viewState.measure(this.docView, i > 0);
5799
- if (!changed && !this.measureRequests.length && this.viewState.scrollTo == null)
5832
+ if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
5800
5833
  break;
5801
5834
  if (i > 5) {
5802
5835
  console.warn("Viewport failed to stabilize");
@@ -5838,9 +5871,9 @@ class EditorView {
5838
5871
  logException(this.state, e);
5839
5872
  }
5840
5873
  }
5841
- if (this.viewState.scrollTo) {
5842
- this.docView.scrollRangeIntoView(this.viewState.scrollTo);
5843
- this.viewState.scrollTo = null;
5874
+ if (this.viewState.scrollTarget) {
5875
+ this.docView.scrollIntoView(this.viewState.scrollTarget);
5876
+ this.viewState.scrollTarget = null;
5844
5877
  }
5845
5878
  if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to && this.measureRequests.length == 0)
5846
5879
  break;
@@ -6063,12 +6096,9 @@ class EditorView {
6063
6096
  moveVertically(start, forward, distance) {
6064
6097
  return skipAtoms(this, start, moveVertically(this, start, forward, distance));
6065
6098
  }
6066
- /**
6067
- Scroll the given document position into view.
6068
- */
6099
+ // FIXME remove on next major version
6069
6100
  scrollPosIntoView(pos) {
6070
- this.viewState.scrollTo = EditorSelection.cursor(pos);
6071
- this.requestMeasure();
6101
+ this.dispatch({ effects: scrollTo.of(EditorSelection.cursor(pos)) });
6072
6102
  }
6073
6103
  /**
6074
6104
  Find the DOM parent node and offset (child offset if `node` is
@@ -6244,6 +6274,11 @@ transaction to make it scroll the given range into view.
6244
6274
  */
6245
6275
  EditorView.scrollTo = scrollTo;
6246
6276
  /**
6277
+ Effect that makes the editor scroll the given range to the
6278
+ center of the visible view.
6279
+ */
6280
+ EditorView.centerOn = centerOn;
6281
+ /**
6247
6282
  Facet to add a [style
6248
6283
  module](https://github.com/marijnh/style-mod#documentation) to
6249
6284
  an editor view. The view will ensure that the module is
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "0.19.10",
3
+ "version": "0.19.11",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",