@codemirror/view 0.19.21 → 0.19.22

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/dist/index.js CHANGED
@@ -1984,10 +1984,7 @@ class DocView extends ContentView {
1984
1984
  let prevDeco = this.decorations, deco = this.updateDeco();
1985
1985
  let decoDiff = findChangedDeco(prevDeco, deco, update.changes);
1986
1986
  changedRanges = ChangedRange.extendWithRanges(changedRanges, decoDiff);
1987
- if (this.dirty == 0 /* Not */ && changedRanges.length == 0 &&
1988
- !(update.flags & 4 /* Viewport */) &&
1989
- update.state.selection.main.from >= this.view.viewport.from &&
1990
- update.state.selection.main.to <= this.view.viewport.to) {
1987
+ if (this.dirty == 0 /* Not */ && changedRanges.length == 0) {
1991
1988
  return false;
1992
1989
  }
1993
1990
  else {
@@ -2010,6 +2007,7 @@ class DocView extends ContentView {
2010
2007
  // Used both by update and checkLayout do perform the actual DOM
2011
2008
  // update
2012
2009
  updateInner(changes, deco, oldLength) {
2010
+ this.view.viewState.mustMeasureContent = true;
2013
2011
  this.updateChildren(changes, deco, oldLength);
2014
2012
  let { observer } = this.view;
2015
2013
  observer.ignore(() => {
@@ -2017,7 +2015,7 @@ class DocView extends ContentView {
2017
2015
  // messes with the scroll position during DOM mutation (though
2018
2016
  // no relayout is triggered and I cannot imagine how it can
2019
2017
  // recompute the scroll position without a layout)
2020
- this.dom.style.height = this.view.viewState.domHeight + "px";
2018
+ this.dom.style.height = this.view.viewState.contentHeight + "px";
2021
2019
  this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
2022
2020
  // Chrome will sometimes, when DOM mutations occur directly
2023
2021
  // around the selection, get confused and report a different
@@ -2302,7 +2300,7 @@ class DocView extends ContentView {
2302
2300
  let next = i == vs.viewports.length ? null : vs.viewports[i];
2303
2301
  let end = next ? next.from - 1 : this.length;
2304
2302
  if (end > pos) {
2305
- let height = vs.lineAt(end, 0).bottom - vs.lineAt(pos, 0).top;
2303
+ let height = vs.lineBlockAt(end).bottom - vs.lineBlockAt(pos).top;
2306
2304
  deco.push(Decoration.replace({ widget: new BlockGapWidget(height), block: true, inclusive: true }).range(pos, end));
2307
2305
  }
2308
2306
  if (!next)
@@ -2908,13 +2906,14 @@ function domPosInText(node, x, y) {
2908
2906
  }
2909
2907
  function posAtCoords(view, { x, y }, precise, bias = -1) {
2910
2908
  var _a;
2911
- let content = view.contentDOM.getBoundingClientRect(), block;
2909
+ let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
2912
2910
  let halfLine = view.defaultLineHeight / 2;
2911
+ let block, yOffset = y - docTop;
2913
2912
  for (let bounced = false;;) {
2914
- block = view.blockAtHeight(y, content.top);
2915
- if (block.top > y || block.bottom < y) {
2916
- bias = block.top > y ? -1 : 1;
2917
- y = Math.min(block.bottom - halfLine, Math.max(block.top + halfLine, y));
2913
+ block = view.elementAtHeight(yOffset);
2914
+ if (block.top > yOffset || block.bottom < yOffset) {
2915
+ bias = block.top > yOffset ? -1 : 1;
2916
+ yOffset = Math.min(block.bottom - halfLine, Math.max(block.top + halfLine, yOffset));
2918
2917
  if (bounced)
2919
2918
  return precise ? null : 0;
2920
2919
  else
@@ -2922,8 +2921,9 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
2922
2921
  }
2923
2922
  if (block.type == BlockType.Text)
2924
2923
  break;
2925
- y = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
2924
+ yOffset = bias > 0 ? block.bottom + halfLine : block.top - halfLine;
2926
2925
  }
2926
+ y = docTop + yOffset;
2927
2927
  let lineStart = block.from;
2928
2928
  // Clip x to the viewport sides
2929
2929
  x = Math.max(content.left + 1, Math.min(content.right - 1, x));
@@ -3036,17 +3036,17 @@ function moveVertically(view, start, forward, distance) {
3036
3036
  return EditorSelection.cursor(startPos);
3037
3037
  let goal = start.goalColumn, startY;
3038
3038
  let rect = view.contentDOM.getBoundingClientRect();
3039
- let startCoords = view.coordsAtPos(startPos);
3039
+ let startCoords = view.coordsAtPos(startPos), docTop = view.documentTop;
3040
3040
  if (startCoords) {
3041
3041
  if (goal == null)
3042
3042
  goal = startCoords.left - rect.left;
3043
3043
  startY = dir < 0 ? startCoords.top : startCoords.bottom;
3044
3044
  }
3045
3045
  else {
3046
- let line = view.viewState.lineAt(startPos, view.dom.getBoundingClientRect().top);
3046
+ let line = view.viewState.lineBlockAt(startPos - docTop);
3047
3047
  if (goal == null)
3048
3048
  goal = Math.min(rect.right - rect.left, view.defaultCharacterWidth * (startPos - line.from));
3049
- startY = dir < 0 ? line.top : line.bottom;
3049
+ startY = (dir < 0 ? line.top : line.bottom) + docTop;
3050
3050
  }
3051
3051
  let resolvedGoal = rect.left + goal;
3052
3052
  let dist = distance !== null && distance !== void 0 ? distance : (view.defaultLineHeight >> 1);
@@ -3297,7 +3297,7 @@ class MouseSelection {
3297
3297
  this.extend = startEvent.shiftKey;
3298
3298
  this.multiple = view.state.facet(EditorState.allowMultipleSelections) && addsSelectionRange(view, startEvent);
3299
3299
  this.dragMove = dragMovesSelection(view, startEvent);
3300
- this.dragging = isInPrimarySelection(view, startEvent) ? null : false;
3300
+ this.dragging = isInPrimarySelection(view, startEvent) && getClickType(startEvent) == 1 ? null : false;
3301
3301
  // When clicking outside of the selection, immediately apply the
3302
3302
  // effect of starting the selection
3303
3303
  if (this.dragging === false) {
@@ -3518,7 +3518,7 @@ function basicMouseSelection(view, event) {
3518
3518
  let last = start, lastEvent = event;
3519
3519
  return {
3520
3520
  update(update) {
3521
- if (update.changes) {
3521
+ if (update.docChanged) {
3522
3522
  if (start)
3523
3523
  start.pos = update.changes.mapPos(start.pos);
3524
3524
  startSel = startSel.map(update.changes);
@@ -3782,7 +3782,10 @@ class HeightOracle {
3782
3782
  return lines * this.lineHeight;
3783
3783
  }
3784
3784
  setDoc(doc) { this.doc = doc; return this; }
3785
- mustRefresh(lineHeights, whiteSpace, direction) {
3785
+ mustRefreshForStyle(whiteSpace, direction) {
3786
+ return (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping || this.direction != direction;
3787
+ }
3788
+ mustRefreshForHeights(lineHeights) {
3786
3789
  let newHeight = false;
3787
3790
  for (let i = 0; i < lineHeights.length; i++) {
3788
3791
  let h = lineHeights[i];
@@ -3794,7 +3797,7 @@ class HeightOracle {
3794
3797
  this.heightSamples[Math.floor(h * 10)] = true;
3795
3798
  }
3796
3799
  }
3797
- return newHeight || (wrappingWhiteSpace.indexOf(whiteSpace) > -1) != this.lineWrapping || this.direction != direction;
3800
+ return newHeight;
3798
3801
  }
3799
3802
  refresh(whiteSpace, direction, lineHeight, charWidth, lineLength, knownHeights) {
3800
3803
  let lineWrapping = wrappingWhiteSpace.indexOf(whiteSpace) > -1;
@@ -3848,7 +3851,8 @@ class BlockInfo {
3848
3851
  */
3849
3852
  length,
3850
3853
  /**
3851
- The top position of the element.
3854
+ The top position of the element (relative to the top of the
3855
+ document).
3852
3856
  */
3853
3857
  top,
3854
3858
  /**
@@ -3882,13 +3886,19 @@ class BlockInfo {
3882
3886
  .concat(Array.isArray(other.type) ? other.type : [other]);
3883
3887
  return new BlockInfo(this.from, this.length + other.length, this.top, this.height + other.height, detail);
3884
3888
  }
3889
+ /**
3890
+ FIXME remove on next breaking release @internal
3891
+ */
3892
+ moveY(offset) {
3893
+ return !offset ? this : new BlockInfo(this.from, this.length, this.top + offset, this.height, Array.isArray(this.type) ? this.type.map(b => b.moveY(offset)) : this.type);
3894
+ }
3885
3895
  }
3886
3896
  var QueryType = /*@__PURE__*/(function (QueryType) {
3887
3897
  QueryType[QueryType["ByPos"] = 0] = "ByPos";
3888
3898
  QueryType[QueryType["ByHeight"] = 1] = "ByHeight";
3889
3899
  QueryType[QueryType["ByPosNoHeight"] = 2] = "ByPosNoHeight";
3890
3900
  return QueryType})(QueryType || (QueryType = {}));
3891
- const Epsilon = 1e-4;
3901
+ const Epsilon = 1e-3;
3892
3902
  class HeightMap {
3893
3903
  constructor(length, // The number of characters covered
3894
3904
  height, // Height of this part of the document
@@ -4115,22 +4125,30 @@ class HeightMapGap extends HeightMap {
4115
4125
  // can't be widgets or collapsed ranges in those lines, because
4116
4126
  // they would already have been added to the heightmap (gaps
4117
4127
  // only contain plain text).
4118
- let nodes = [], pos = Math.max(offset, measured.from);
4128
+ let nodes = [], pos = Math.max(offset, measured.from), singleHeight = -1;
4129
+ let wasChanged = oracle.heightChanged;
4119
4130
  if (measured.from > offset)
4120
4131
  nodes.push(new HeightMapGap(measured.from - offset - 1).updateHeight(oracle, offset));
4121
4132
  while (pos <= end && measured.more) {
4122
4133
  let len = oracle.doc.lineAt(pos).length;
4123
4134
  if (nodes.length)
4124
4135
  nodes.push(null);
4125
- let line = new HeightMapText(len, measured.heights[measured.index++]);
4136
+ let height = measured.heights[measured.index++];
4137
+ if (singleHeight == -1)
4138
+ singleHeight = height;
4139
+ else if (Math.abs(height - singleHeight) >= Epsilon)
4140
+ singleHeight = -2;
4141
+ let line = new HeightMapText(len, height);
4126
4142
  line.outdated = false;
4127
4143
  nodes.push(line);
4128
4144
  pos += len + 1;
4129
4145
  }
4130
4146
  if (pos <= end)
4131
4147
  nodes.push(null, new HeightMapGap(end - pos).updateHeight(oracle, pos));
4132
- oracle.heightChanged = true;
4133
- return HeightMap.of(nodes);
4148
+ let result = HeightMap.of(nodes);
4149
+ oracle.heightChanged = wasChanged || singleHeight < 0 || Math.abs(result.height - this.height) >= Epsilon ||
4150
+ Math.abs(singleHeight - this.lines(oracle.doc, offset).lineHeight) >= Epsilon;
4151
+ return result;
4134
4152
  }
4135
4153
  else if (force || this.outdated) {
4136
4154
  this.setHeight(oracle, oracle.heightForGap(offset, offset + this.length));
@@ -4488,7 +4506,8 @@ class ViewState {
4488
4506
  this.inView = true;
4489
4507
  this.paddingTop = 0;
4490
4508
  this.paddingBottom = 0;
4491
- this.contentWidth = 0;
4509
+ this.contentDOMWidth = 0;
4510
+ this.contentDOMHeight = 0;
4492
4511
  this.editorHeight = 0;
4493
4512
  this.heightOracle = new HeightOracle;
4494
4513
  // See VP.MaxDOMHeight
@@ -4496,6 +4515,9 @@ class ViewState {
4496
4515
  this.scrollTarget = null;
4497
4516
  // Briefly set to true when printing, to disable viewport limiting
4498
4517
  this.printing = false;
4518
+ // Flag set when editor content was redrawn, so that the next
4519
+ // measure stage knows it must read DOM layout
4520
+ this.mustMeasureContent = true;
4499
4521
  this.visibleRanges = [];
4500
4522
  // Cursor 'assoc' is only significant when the cursor is on a line
4501
4523
  // wrap point, where it must stick to the character that it is
@@ -4508,6 +4530,7 @@ class ViewState {
4508
4530
  this.mustEnforceCursorAssoc = false;
4509
4531
  this.heightMap = HeightMap.empty().applyChanges(state.facet(decorations), Text.empty, this.heightOracle.setDoc(state.doc), [new ChangedRange(0, 0, 0, state.doc.length)]);
4510
4532
  this.viewport = this.getViewport(0, null);
4533
+ this.updateViewportLines();
4511
4534
  this.updateForViewport();
4512
4535
  this.lineGaps = this.ensureLineGaps([]);
4513
4536
  this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(false)));
@@ -4518,7 +4541,7 @@ class ViewState {
4518
4541
  for (let i = 0; i <= 1; i++) {
4519
4542
  let pos = i ? main.head : main.anchor;
4520
4543
  if (!viewports.some(({ from, to }) => pos >= from && pos <= to)) {
4521
- let { from, to } = this.lineAt(pos, 0);
4544
+ let { from, to } = this.lineBlockAt(pos);
4522
4545
  viewports.push(new Viewport(from, to));
4523
4546
  }
4524
4547
  }
@@ -4526,6 +4549,12 @@ class ViewState {
4526
4549
  this.scaler = this.heightMap.height <= 7000000 /* MaxDOMHeight */ ? IdScaler :
4527
4550
  new BigScaler(this.heightOracle.doc, this.heightMap, this.viewports);
4528
4551
  }
4552
+ updateViewportLines() {
4553
+ this.viewportLines = [];
4554
+ this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.state.doc, 0, 0, block => {
4555
+ this.viewportLines.push(this.scaler.scale == 1 ? block : scaleBlock(block, this.scaler));
4556
+ });
4557
+ }
4529
4558
  update(update, scrollTarget = null) {
4530
4559
  let prev = this.state;
4531
4560
  this.state = update.state;
@@ -4540,6 +4569,9 @@ class ViewState {
4540
4569
  if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
4541
4570
  !this.viewportIsAppropriate(viewport))
4542
4571
  viewport = this.getViewport(0, scrollTarget);
4572
+ if (!update.changes.empty || (update.flags & 2 /* Height */) ||
4573
+ viewport.from != this.viewport.from || viewport.to != this.viewport.to)
4574
+ this.updateViewportLines();
4543
4575
  this.viewport = viewport;
4544
4576
  this.updateForViewport();
4545
4577
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
@@ -4551,13 +4583,17 @@ class ViewState {
4551
4583
  update.state.selection.main.empty && update.state.selection.main.assoc)
4552
4584
  this.mustEnforceCursorAssoc = true;
4553
4585
  }
4554
- measure(docView, repeated) {
4555
- let dom = docView.dom, whiteSpace = "", direction = Direction.LTR;
4556
- let result = 0;
4557
- if (!repeated) {
4586
+ measure(view) {
4587
+ let dom = view.contentDOM, style = window.getComputedStyle(dom);
4588
+ let oracle = this.heightOracle;
4589
+ let whiteSpace = style.whiteSpace, direction = style.direction == "rtl" ? Direction.RTL : Direction.LTR;
4590
+ let refresh = this.heightOracle.mustRefreshForStyle(whiteSpace, direction);
4591
+ let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
4592
+ let result = 0, bias = 0;
4593
+ if (measureContent) {
4594
+ this.mustMeasureContent = false;
4595
+ this.contentDOMHeight = dom.clientHeight;
4558
4596
  // Vertical padding
4559
- let style = window.getComputedStyle(dom);
4560
- whiteSpace = style.whiteSpace, direction = (style.direction == "rtl" ? Direction.RTL : Direction.LTR);
4561
4597
  let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
4562
4598
  if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
4563
4599
  result |= 8 /* Geometry */;
@@ -4572,39 +4608,42 @@ class ViewState {
4572
4608
  this.inView = this.pixelViewport.bottom > this.pixelViewport.top && this.pixelViewport.right > this.pixelViewport.left;
4573
4609
  if (!this.inView)
4574
4610
  return 0;
4575
- let lineHeights = docView.measureVisibleLineHeights();
4576
- let refresh = false, bias = 0, oracle = this.heightOracle;
4577
- if (!repeated) {
4578
- let contentWidth = docView.dom.clientWidth;
4579
- if (oracle.mustRefresh(lineHeights, whiteSpace, direction) ||
4580
- oracle.lineWrapping && Math.abs(contentWidth - this.contentWidth) > oracle.charWidth) {
4581
- let { lineHeight, charWidth } = docView.measureTextSize();
4611
+ if (measureContent) {
4612
+ let lineHeights = view.docView.measureVisibleLineHeights();
4613
+ if (oracle.mustRefreshForHeights(lineHeights))
4614
+ refresh = true;
4615
+ let contentWidth = dom.clientWidth;
4616
+ if (refresh || oracle.lineWrapping && Math.abs(contentWidth - this.contentDOMWidth) > oracle.charWidth) {
4617
+ let { lineHeight, charWidth } = view.docView.measureTextSize();
4582
4618
  refresh = oracle.refresh(whiteSpace, direction, lineHeight, charWidth, contentWidth / charWidth, lineHeights);
4583
4619
  if (refresh) {
4584
- docView.minWidth = 0;
4620
+ view.docView.minWidth = 0;
4585
4621
  result |= 8 /* Geometry */;
4586
4622
  }
4587
4623
  }
4588
- if (this.contentWidth != contentWidth) {
4589
- this.contentWidth = contentWidth;
4624
+ if (this.contentDOMWidth != contentWidth) {
4625
+ this.contentDOMWidth = contentWidth;
4590
4626
  result |= 8 /* Geometry */;
4591
4627
  }
4592
- if (this.editorHeight != docView.view.scrollDOM.clientHeight) {
4593
- this.editorHeight = docView.view.scrollDOM.clientHeight;
4628
+ if (this.editorHeight != view.scrollDOM.clientHeight) {
4629
+ this.editorHeight = view.scrollDOM.clientHeight;
4594
4630
  result |= 8 /* Geometry */;
4595
4631
  }
4596
4632
  if (dTop > 0 && dBottom > 0)
4597
4633
  bias = Math.max(dTop, dBottom);
4598
4634
  else if (dTop < 0 && dBottom < 0)
4599
4635
  bias = Math.min(dTop, dBottom);
4600
- }
4601
- oracle.heightChanged = false;
4602
- this.heightMap = this.heightMap.updateHeight(oracle, 0, refresh, new MeasuredHeights(this.viewport.from, lineHeights));
4603
- if (oracle.heightChanged)
4604
- result |= 2 /* Height */;
4605
- if (!this.viewportIsAppropriate(this.viewport, bias) ||
4606
- this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to))
4636
+ oracle.heightChanged = false;
4637
+ this.heightMap = this.heightMap.updateHeight(oracle, 0, refresh, new MeasuredHeights(this.viewport.from, lineHeights));
4638
+ if (oracle.heightChanged)
4639
+ result |= 2 /* Height */;
4640
+ }
4641
+ let viewportChange = !this.viewportIsAppropriate(this.viewport, bias) ||
4642
+ this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to);
4643
+ if (viewportChange)
4607
4644
  this.viewport = this.getViewport(bias, this.scrollTarget);
4645
+ if ((result & 2 /* Height */) || viewportChange)
4646
+ this.updateViewportLines();
4608
4647
  this.updateForViewport();
4609
4648
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > 4000 /* DoubleMargin */)
4610
4649
  this.updateLineGaps(this.ensureLineGaps(refresh ? [] : this.lineGaps));
@@ -4615,12 +4654,12 @@ class ViewState {
4615
4654
  // to a line end is going to trigger a layout anyway, so it
4616
4655
  // can't be a pure write. It should be rare that it does any
4617
4656
  // writing.
4618
- docView.enforceCursorAssoc();
4657
+ view.docView.enforceCursorAssoc();
4619
4658
  }
4620
4659
  return result;
4621
4660
  }
4622
- get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top, 0); }
4623
- get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom, 0); }
4661
+ get visibleTop() { return this.scaler.fromDOM(this.pixelViewport.top); }
4662
+ get visibleBottom() { return this.scaler.fromDOM(this.pixelViewport.bottom); }
4624
4663
  getViewport(bias, scrollTarget) {
4625
4664
  // This will divide VP.Margin between the top and the
4626
4665
  // bottom, depending on the bias (the change in viewport position
@@ -4680,12 +4719,12 @@ class ViewState {
4680
4719
  // This won't work at all in predominantly right-to-left text.
4681
4720
  if (this.heightOracle.direction != Direction.LTR)
4682
4721
  return gaps;
4683
- this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.state.doc, 0, 0, line => {
4722
+ for (let line of this.viewportLines) {
4684
4723
  if (line.length < 4000 /* DoubleMargin */)
4685
- return;
4724
+ continue;
4686
4725
  let structure = lineStructure(line.from, line.to, this.state);
4687
4726
  if (structure.total < 4000 /* DoubleMargin */)
4688
- return;
4727
+ continue;
4689
4728
  let viewFrom, viewTo;
4690
4729
  if (this.heightOracle.lineWrapping) {
4691
4730
  let marginHeight = (2000 /* Margin */ / this.heightOracle.lineLength) * this.heightOracle.lineHeight;
@@ -4715,7 +4754,7 @@ class ViewState {
4715
4754
  Math.abs(gap.from - from) < 1000 /* HalfMargin */ && Math.abs(gap.to - to) < 1000 /* HalfMargin */) ||
4716
4755
  new LineGap(from, to, this.gapSize(line, from, to, structure)));
4717
4756
  }
4718
- });
4757
+ }
4719
4758
  return gaps;
4720
4759
  }
4721
4760
  gapSize(line, from, to, structure) {
@@ -4747,27 +4786,18 @@ class ViewState {
4747
4786
  this.visibleRanges = ranges;
4748
4787
  return changed ? 4 /* Viewport */ : 0;
4749
4788
  }
4750
- lineAt(pos, editorTop) {
4751
- editorTop += this.paddingTop;
4752
- return scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.state.doc, editorTop, 0), this.scaler, editorTop);
4789
+ lineBlockAt(pos) {
4790
+ return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to <= pos)) ||
4791
+ scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.state.doc, 0, 0), this.scaler);
4753
4792
  }
4754
- lineAtHeight(height, editorTop) {
4755
- editorTop += this.paddingTop;
4756
- return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height, editorTop), QueryType.ByHeight, this.state.doc, editorTop, 0), this.scaler, editorTop);
4793
+ lineBlockAtHeight(height) {
4794
+ return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.state.doc, 0, 0), this.scaler);
4757
4795
  }
4758
- blockAtHeight(height, editorTop) {
4759
- editorTop += this.paddingTop;
4760
- return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height, editorTop), this.state.doc, editorTop, 0), this.scaler, editorTop);
4761
- }
4762
- forEachLine(from, to, f, editorTop) {
4763
- editorTop += this.paddingTop;
4764
- return this.heightMap.forEachLine(from, to, this.state.doc, editorTop, 0, this.scaler.scale == 1 ? f : b => f(scaleBlock(b, this.scaler, editorTop)));
4796
+ elementAtHeight(height) {
4797
+ return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.state.doc, 0, 0), this.scaler);
4765
4798
  }
4766
4799
  get contentHeight() {
4767
- return this.domHeight + this.paddingTop + this.paddingBottom;
4768
- }
4769
- get domHeight() {
4770
- return this.scaler.toDOM(this.heightMap.height, this.paddingTop);
4800
+ return this.scaler.toDOM(this.heightMap.height) + this.paddingTop + this.paddingBottom;
4771
4801
  }
4772
4802
  }
4773
4803
  class Viewport {
@@ -4864,36 +4894,34 @@ class BigScaler {
4864
4894
  base = obj.bottom;
4865
4895
  }
4866
4896
  }
4867
- toDOM(n, top) {
4868
- n -= top;
4897
+ toDOM(n) {
4869
4898
  for (let i = 0, base = 0, domBase = 0;; i++) {
4870
4899
  let vp = i < this.viewports.length ? this.viewports[i] : null;
4871
4900
  if (!vp || n < vp.top)
4872
- return domBase + (n - base) * this.scale + top;
4901
+ return domBase + (n - base) * this.scale;
4873
4902
  if (n <= vp.bottom)
4874
- return vp.domTop + (n - vp.top) + top;
4903
+ return vp.domTop + (n - vp.top);
4875
4904
  base = vp.bottom;
4876
4905
  domBase = vp.domBottom;
4877
4906
  }
4878
4907
  }
4879
- fromDOM(n, top) {
4880
- n -= top;
4908
+ fromDOM(n) {
4881
4909
  for (let i = 0, base = 0, domBase = 0;; i++) {
4882
4910
  let vp = i < this.viewports.length ? this.viewports[i] : null;
4883
4911
  if (!vp || n < vp.domTop)
4884
- return base + (n - domBase) / this.scale + top;
4912
+ return base + (n - domBase) / this.scale;
4885
4913
  if (n <= vp.domBottom)
4886
- return vp.top + (n - vp.domTop) + top;
4914
+ return vp.top + (n - vp.domTop);
4887
4915
  base = vp.bottom;
4888
4916
  domBase = vp.domBottom;
4889
4917
  }
4890
4918
  }
4891
4919
  }
4892
- function scaleBlock(block, scaler, top) {
4920
+ function scaleBlock(block, scaler) {
4893
4921
  if (scaler.scale == 1)
4894
4922
  return block;
4895
- let bTop = scaler.toDOM(block.top, top), bBottom = scaler.toDOM(block.bottom, top);
4896
- return new BlockInfo(block.from, block.length, bTop, bBottom - bTop, Array.isArray(block.type) ? block.type.map(b => scaleBlock(b, scaler, top)) : block.type);
4923
+ let bTop = scaler.toDOM(block.top), bBottom = scaler.toDOM(block.bottom);
4924
+ return new BlockInfo(block.from, block.length, bTop, bBottom - bTop, Array.isArray(block.type) ? block.type.map(b => scaleBlock(b, scaler)) : block.type);
4897
4925
  }
4898
4926
 
4899
4927
  const theme = /*@__PURE__*/Facet.define({ combine: strs => strs.join(" ") });
@@ -5872,11 +5900,11 @@ class EditorView {
5872
5900
  for (let i = 0;; i++) {
5873
5901
  this.updateState = 1 /* Measuring */;
5874
5902
  let oldViewport = this.viewport;
5875
- let changed = this.viewState.measure(this.docView, i > 0);
5903
+ let changed = this.viewState.measure(this);
5876
5904
  if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
5877
5905
  break;
5878
5906
  if (i > 5) {
5879
- console.warn("Viewport failed to stabilize");
5907
+ console.warn(this.measureRequests.length ? "Measure loop restarted more than 5 times" : "Viewport failed to stabilize");
5880
5908
  break;
5881
5909
  }
5882
5910
  let measuring = [];
@@ -5892,7 +5920,7 @@ class EditorView {
5892
5920
  return BadMeasure;
5893
5921
  }
5894
5922
  });
5895
- let update = new ViewUpdate(this, this.state);
5923
+ let update = new ViewUpdate(this, this.state), redrawn = false;
5896
5924
  update.flags |= changed;
5897
5925
  if (!updated)
5898
5926
  updated = update;
@@ -5902,13 +5930,15 @@ class EditorView {
5902
5930
  if (!update.empty) {
5903
5931
  this.updatePlugins(update);
5904
5932
  this.inputState.update(update);
5933
+ this.updateAttrs();
5934
+ redrawn = this.docView.update(update);
5905
5935
  }
5906
- this.updateAttrs();
5907
- let redrawn = changed > 0 && this.docView.update(update);
5908
5936
  for (let i = 0; i < measuring.length; i++)
5909
5937
  if (measured[i] != BadMeasure) {
5910
5938
  try {
5911
- measuring[i].write(measured[i], this);
5939
+ let m = measuring[i];
5940
+ if (m.write)
5941
+ m.write(measured[i], this);
5912
5942
  }
5913
5943
  catch (e) {
5914
5944
  logException(this.state, e);
@@ -5918,8 +5948,8 @@ class EditorView {
5918
5948
  this.docView.scrollIntoView(this.viewState.scrollTarget);
5919
5949
  this.viewState.scrollTarget = null;
5920
5950
  }
5921
- if (changed)
5922
- this.docView.updateSelection(redrawn);
5951
+ if (redrawn)
5952
+ this.docView.updateSelection(true);
5923
5953
  if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to && this.measureRequests.length == 0)
5924
5954
  break;
5925
5955
  }
@@ -6032,6 +6062,13 @@ class EditorView {
6032
6062
  return null;
6033
6063
  }
6034
6064
  /**
6065
+ The top position of the document, in screen coordinates. This
6066
+ may be negative when the editor is scrolled down.
6067
+ */
6068
+ get documentTop() {
6069
+ return this.contentDOM.getBoundingClientRect().top + this.viewState.paddingTop;
6070
+ }
6071
+ /**
6035
6072
  Find the line or block widget at the given vertical position.
6036
6073
 
6037
6074
  By default, this position is interpreted as a screen position,
@@ -6041,10 +6078,21 @@ class EditorView {
6041
6078
  position, or a precomputed document top
6042
6079
  (`view.contentDOM.getBoundingClientRect().top`) to limit layout
6043
6080
  queries.
6081
+
6082
+ *Deprecated: use `blockAtHeight` instead.*
6044
6083
  */
6045
6084
  blockAtHeight(height, docTop) {
6085
+ let top = ensureTop(docTop, this);
6086
+ return this.elementAtHeight(height - top).moveY(top);
6087
+ }
6088
+ /**
6089
+ Find the text line or block widget at the given vertical
6090
+ position (which is interpreted as relative to the [top of the
6091
+ document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop)
6092
+ */
6093
+ elementAtHeight(height) {
6046
6094
  this.readMeasured();
6047
- return this.viewState.blockAtHeight(height, ensureTop(docTop, this.contentDOM));
6095
+ return this.viewState.elementAtHeight(height);
6048
6096
  }
6049
6097
  /**
6050
6098
  Find information for the visual line (see
@@ -6056,20 +6104,43 @@ class EditorView {
6056
6104
  Defaults to treating `height` as a screen position. See
6057
6105
  [`blockAtHeight`](https://codemirror.net/6/docs/ref/#view.EditorView.blockAtHeight) for the
6058
6106
  interpretation of the `docTop` parameter.
6107
+
6108
+ *Deprecated: use `lineBlockAtHeight` instead.*
6059
6109
  */
6060
6110
  visualLineAtHeight(height, docTop) {
6111
+ let top = ensureTop(docTop, this);
6112
+ return this.lineBlockAtHeight(height - top).moveY(top);
6113
+ }
6114
+ /**
6115
+ Find the line block (see
6116
+ [`lineBlockAt`](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) at the given
6117
+ height.
6118
+ */
6119
+ lineBlockAtHeight(height) {
6061
6120
  this.readMeasured();
6062
- return this.viewState.lineAtHeight(height, ensureTop(docTop, this.contentDOM));
6121
+ return this.viewState.lineBlockAtHeight(height);
6063
6122
  }
6064
6123
  /**
6065
6124
  Iterate over the height information of the visual lines in the
6066
6125
  viewport. The heights of lines are reported relative to the
6067
6126
  given document top, which defaults to the screen position of the
6068
6127
  document (forcing a layout).
6128
+
6129
+ *Deprecated: use `viewportLineBlocks` instead.*
6069
6130
  */
6070
6131
  viewportLines(f, docTop) {
6071
- let { from, to } = this.viewport;
6072
- this.viewState.forEachLine(from, to, f, ensureTop(docTop, this.contentDOM));
6132
+ let top = ensureTop(docTop, this);
6133
+ for (let line of this.viewportLineBlocks)
6134
+ f(line.moveY(top));
6135
+ }
6136
+ /**
6137
+ Get the extent and vertical position of all [line
6138
+ blocks](https://codemirror.net/6/docs/ref/#view.EditorView.lineBlockAt) in the viewport. Positions
6139
+ are relative to the [top of the
6140
+ document](https://codemirror.net/6/docs/ref/#view.EditorView.documentTop);
6141
+ */
6142
+ get viewportLineBlocks() {
6143
+ return this.viewState.viewportLines;
6073
6144
  }
6074
6145
  /**
6075
6146
  Find the extent and height of the visual line (a range delimited
@@ -6080,9 +6151,22 @@ class EditorView {
6080
6151
  argument, which defaults to 0 for this method. You can pass
6081
6152
  `view.contentDOM.getBoundingClientRect().top` here to get screen
6082
6153
  coordinates.
6154
+
6155
+ *Deprecated: use `lineBlockAt` instead.*
6083
6156
  */
6084
6157
  visualLineAt(pos, docTop = 0) {
6085
- return this.viewState.lineAt(pos, docTop);
6158
+ return this.lineBlockAt(pos).moveY(docTop + this.viewState.paddingTop);
6159
+ }
6160
+ /**
6161
+ Find the line block around the given document position. A line
6162
+ block is a range delimited on both sides by either a
6163
+ non-[hidden](https://codemirror.net/6/docs/ref/#view.Decoration^range) line breaks, or the
6164
+ start/end of the document. It will usually just hold a line of
6165
+ text, but may be broken into multiple textblocks by block
6166
+ widgets.
6167
+ */
6168
+ lineBlockAt(pos) {
6169
+ return this.viewState.lineBlockAt(pos);
6086
6170
  }
6087
6171
  /**
6088
6172
  The editor's total content height.
@@ -6415,8 +6499,9 @@ search match).
6415
6499
  EditorView.announce = /*@__PURE__*/StateEffect.define();
6416
6500
  // Maximum line length for which we compute accurate bidi info
6417
6501
  const MaxBidiLine = 4096;
6418
- function ensureTop(given, dom) {
6419
- return given == null ? dom.getBoundingClientRect().top : given;
6502
+ // FIXME remove this and its callers on next breaking release
6503
+ function ensureTop(given, view) {
6504
+ return (given == null ? view.contentDOM.getBoundingClientRect().top : given) + view.viewState.paddingTop;
6420
6505
  }
6421
6506
  let resizeDebounce = -1;
6422
6507
  function ensureGlobalHandler() {
@@ -6767,7 +6852,7 @@ function wrappedLine(view, pos, inside) {
6767
6852
  type: BlockType.Text };
6768
6853
  }
6769
6854
  function blockAt(view, pos) {
6770
- let line = view.visualLineAt(pos);
6855
+ let line = view.lineBlockAt(pos);
6771
6856
  if (Array.isArray(line.type))
6772
6857
  for (let l of line.type) {
6773
6858
  if (l.to > pos || l.to == pos && (l.to == line.to || l.type == BlockType.Text))
@@ -7158,7 +7243,7 @@ const activeLineHighlighter = /*@__PURE__*/ViewPlugin.fromClass(class {
7158
7243
  for (let r of view.state.selection.ranges) {
7159
7244
  if (!r.empty)
7160
7245
  return Decoration.none;
7161
- let line = view.visualLineAt(r.head);
7246
+ let line = view.lineBlockAt(r.head);
7162
7247
  if (line.from > lastLineStart) {
7163
7248
  deco.push(lineDeco.range(line.from));
7164
7249
  lastLineStart = line.from;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "0.19.21",
3
+ "version": "0.19.22",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",