@codemirror/view 6.8.1 → 6.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,25 @@
1
+ ## 6.9.1 (2023-02-17)
2
+
3
+ ### Bug fixes
4
+
5
+ Improve the way `posAtCoords` picks the side of a widget to return by comparing the coordinates the center of the widget.
6
+
7
+ Fix an issue where transactions created for the `focusChangeEffect` facet were sometimes not dispatched.
8
+
9
+ ## 6.9.0 (2023-02-15)
10
+
11
+ ### Bug fixes
12
+
13
+ Fix an issue where inaccurate estimated vertical positions could cause the viewport to not converge in line-wrapped editors.
14
+
15
+ Don't suppress double-space to period conversion when autocorrect is enabled.
16
+
17
+ Make sure the measuring code notices when the scaling of the editor is changed, and does a full measure in that case.
18
+
19
+ ### New features
20
+
21
+ The new `EditorView.focusChangeEffect` facet can be used to dispatch a state effect when the editor is focused or blurred.
22
+
1
23
  ## 6.8.1 (2023-02-08)
2
24
 
3
25
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -519,6 +519,7 @@ class ContentView {
519
519
  }
520
520
  static get(node) { return node.cmView; }
521
521
  get isEditable() { return true; }
522
+ get isWidget() { return false; }
522
523
  merge(from, to, source, hasStart, openStart, openEnd) {
523
524
  return false;
524
525
  }
@@ -893,6 +894,7 @@ class WidgetView extends ContentView {
893
894
  return this.length ? rect : flattenRect(rect, this.side > 0);
894
895
  }
895
896
  get isEditable() { return false; }
897
+ get isWidget() { return true; }
896
898
  destroy() {
897
899
  super.destroy();
898
900
  if (this.dom)
@@ -1603,6 +1605,8 @@ class BlockWidgetView extends ContentView {
1603
1605
  }
1604
1606
  ignoreMutation() { return true; }
1605
1607
  ignoreEvent(event) { return this.widget.ignoreEvent(event); }
1608
+ get isEditable() { return false; }
1609
+ get isWidget() { return true; }
1606
1610
  destroy() {
1607
1611
  super.destroy();
1608
1612
  if (this.dom)
@@ -1784,6 +1788,7 @@ const mouseSelectionStyle = state.Facet.define();
1784
1788
  const exceptionSink = state.Facet.define();
1785
1789
  const updateListener = state.Facet.define();
1786
1790
  const inputHandler = state.Facet.define();
1791
+ const focusChangeEffect = state.Facet.define();
1787
1792
  const perLineTextDirection = state.Facet.define({
1788
1793
  combine: values => values.some(x => x)
1789
1794
  });
@@ -2026,11 +2031,6 @@ class ViewUpdate {
2026
2031
  let changedRanges = [];
2027
2032
  this.changes.iterChangedRanges((fromA, toA, fromB, toB) => changedRanges.push(new ChangedRange(fromA, toA, fromB, toB)));
2028
2033
  this.changedRanges = changedRanges;
2029
- let focus = view.hasFocus;
2030
- if (focus != view.inputState.notifiedFocused) {
2031
- view.inputState.notifiedFocused = focus;
2032
- this.flags |= 1 /* UpdateFlag.Focus */;
2033
- }
2034
2034
  }
2035
2035
  /**
2036
2036
  @internal
@@ -3166,11 +3166,11 @@ function domPosInText(node, x, y) {
3166
3166
  }
3167
3167
  return { node, offset: closestOffset > -1 ? closestOffset : generalSide > 0 ? node.nodeValue.length : 0 };
3168
3168
  }
3169
- function posAtCoords(view, { x, y }, precise, bias = -1) {
3169
+ function posAtCoords(view, coords, precise, bias = -1) {
3170
3170
  var _a;
3171
3171
  let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
3172
3172
  let block, { docHeight } = view.viewState;
3173
- let yOffset = y - docTop;
3173
+ let { x, y } = coords, yOffset = y - docTop;
3174
3174
  if (yOffset < 0)
3175
3175
  return 0;
3176
3176
  if (yOffset > docHeight)
@@ -3241,7 +3241,17 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3241
3241
  return yOffset > block.top + block.height / 2 ? block.to : block.from;
3242
3242
  ({ node, offset } = domPosAtCoords(line.dom, x, y));
3243
3243
  }
3244
- return view.docView.posFromDOM(node, offset);
3244
+ let nearest = view.docView.nearest(node);
3245
+ if (!nearest)
3246
+ return null;
3247
+ if (nearest.isWidget) {
3248
+ let rect = nearest.dom.getBoundingClientRect();
3249
+ return coords.y < rect.top || coords.y <= rect.bottom && coords.x <= (rect.left + rect.right) / 2
3250
+ ? nearest.posAtStart : nearest.posAtEnd;
3251
+ }
3252
+ else {
3253
+ return nearest.localPosFromDOM(node, offset) + nearest.posAtStart;
3254
+ }
3245
3255
  }
3246
3256
  function posAtCoordsImprecise(view, contentRect, block, x, y) {
3247
3257
  let into = Math.round((x - contentRect.left) * view.defaultCharacterWidth);
@@ -4032,10 +4042,26 @@ handlers.copy = handlers.cut = (view, event) => {
4032
4042
  userEvent: "delete.cut"
4033
4043
  });
4034
4044
  };
4045
+ const isFocusChange = state.Annotation.define();
4046
+ function focusChangeTransaction(state, focus) {
4047
+ let effects = [];
4048
+ for (let getEffect of state.facet(focusChangeEffect)) {
4049
+ let effect = getEffect(state, focus);
4050
+ if (effect)
4051
+ effects.push(effect);
4052
+ }
4053
+ return effects ? state.update({ effects, annotations: isFocusChange.of(true) }) : null;
4054
+ }
4035
4055
  function updateForFocusChange(view) {
4036
4056
  setTimeout(() => {
4037
- if (view.hasFocus != view.inputState.notifiedFocused)
4038
- view.update([]);
4057
+ let focus = view.hasFocus;
4058
+ if (focus != view.inputState.notifiedFocused) {
4059
+ let tr = focusChangeTransaction(view.state, focus);
4060
+ if (tr)
4061
+ view.dispatch(tr);
4062
+ else
4063
+ view.update([]);
4064
+ }
4039
4065
  }, 10);
4040
4066
  }
4041
4067
  handlers.focus = view => {
@@ -4117,7 +4143,7 @@ class HeightOracle {
4117
4143
  heightForGap(from, to) {
4118
4144
  let lines = this.doc.lineAt(to).number - this.doc.lineAt(from).number + 1;
4119
4145
  if (this.lineWrapping)
4120
- lines += Math.ceil(((to - from) - (lines * this.lineLength * 0.5)) / this.lineLength);
4146
+ lines += Math.max(0, Math.ceil(((to - from) - (lines * this.lineLength * 0.5)) / this.lineLength));
4121
4147
  return this.lineHeight * lines;
4122
4148
  }
4123
4149
  heightForLine(length) {
@@ -4263,11 +4289,11 @@ class HeightMap {
4263
4289
  decomposeLeft(_to, result) { result.push(this); }
4264
4290
  decomposeRight(_from, result) { result.push(this); }
4265
4291
  applyChanges(decorations, oldDoc, oracle, changes) {
4266
- let me = this;
4292
+ let me = this, doc = oracle.doc;
4267
4293
  for (let i = changes.length - 1; i >= 0; i--) {
4268
4294
  let { fromA, toA, fromB, toB } = changes[i];
4269
- let start = me.lineAt(fromA, QueryType.ByPosNoHeight, oldDoc, 0, 0);
4270
- let end = start.to >= toA ? start : me.lineAt(toA, QueryType.ByPosNoHeight, oldDoc, 0, 0);
4295
+ let start = me.lineAt(fromA, QueryType.ByPosNoHeight, oracle.setDoc(oldDoc), 0, 0);
4296
+ let end = start.to >= toA ? start : me.lineAt(toA, QueryType.ByPosNoHeight, oracle, 0, 0);
4271
4297
  toB += end.to - toA;
4272
4298
  toA = end.to;
4273
4299
  while (i > 0 && start.from <= changes[i - 1].toA) {
@@ -4275,11 +4301,11 @@ class HeightMap {
4275
4301
  fromB = changes[i - 1].fromB;
4276
4302
  i--;
4277
4303
  if (fromA < start.from)
4278
- start = me.lineAt(fromA, QueryType.ByPosNoHeight, oldDoc, 0, 0);
4304
+ start = me.lineAt(fromA, QueryType.ByPosNoHeight, oracle, 0, 0);
4279
4305
  }
4280
4306
  fromB += start.from - fromA;
4281
4307
  fromA = start.from;
4282
- let nodes = NodeBuilder.build(oracle, decorations, fromB, toB);
4308
+ let nodes = NodeBuilder.build(oracle.setDoc(doc), decorations, fromB, toB);
4283
4309
  me = me.replace(fromA, toA, nodes);
4284
4310
  }
4285
4311
  return me.updateHeight(oracle, 0);
@@ -4346,15 +4372,15 @@ class HeightMapBlock extends HeightMap {
4346
4372
  super(length, height);
4347
4373
  this.type = type;
4348
4374
  }
4349
- blockAt(_height, _doc, top, offset) {
4375
+ blockAt(_height, _oracle, top, offset) {
4350
4376
  return new BlockInfo(offset, this.length, top, this.height, this.type);
4351
4377
  }
4352
- lineAt(_value, _type, doc, top, offset) {
4353
- return this.blockAt(0, doc, top, offset);
4378
+ lineAt(_value, _type, oracle, top, offset) {
4379
+ return this.blockAt(0, oracle, top, offset);
4354
4380
  }
4355
- forEachLine(from, to, doc, top, offset, f) {
4381
+ forEachLine(from, to, oracle, top, offset, f) {
4356
4382
  if (from <= offset + this.length && to >= offset)
4357
- f(this.blockAt(0, doc, top, offset));
4383
+ f(this.blockAt(0, oracle, top, offset));
4358
4384
  }
4359
4385
  updateHeight(oracle, offset = 0, _force = false, measured) {
4360
4386
  if (measured && measured.from <= offset && measured.more)
@@ -4400,35 +4426,60 @@ class HeightMapText extends HeightMapBlock {
4400
4426
  }
4401
4427
  class HeightMapGap extends HeightMap {
4402
4428
  constructor(length) { super(length, 0); }
4403
- lines(doc, offset) {
4404
- let firstLine = doc.lineAt(offset).number, lastLine = doc.lineAt(offset + this.length).number;
4405
- return { firstLine, lastLine, lineHeight: this.height / (lastLine - firstLine + 1) };
4429
+ heightMetrics(oracle, offset) {
4430
+ let firstLine = oracle.doc.lineAt(offset).number, lastLine = oracle.doc.lineAt(offset + this.length).number;
4431
+ let lines = lastLine - firstLine + 1;
4432
+ let perLine, perChar = 0;
4433
+ if (oracle.lineWrapping) {
4434
+ let totalPerLine = Math.min(this.height, oracle.lineHeight * lines);
4435
+ perLine = totalPerLine / lines;
4436
+ perChar = (this.height - totalPerLine) / (this.length - lines - 1);
4437
+ }
4438
+ else {
4439
+ perLine = this.height / lines;
4440
+ }
4441
+ return { firstLine, lastLine, perLine, perChar };
4406
4442
  }
4407
- blockAt(height, doc, top, offset) {
4408
- let { firstLine, lastLine, lineHeight } = this.lines(doc, offset);
4409
- let line = Math.max(0, Math.min(lastLine - firstLine, Math.floor((height - top) / lineHeight)));
4410
- let { from, length } = doc.line(firstLine + line);
4411
- return new BlockInfo(from, length, top + lineHeight * line, lineHeight, exports.BlockType.Text);
4443
+ blockAt(height, oracle, top, offset) {
4444
+ let { firstLine, lastLine, perLine, perChar } = this.heightMetrics(oracle, offset);
4445
+ if (oracle.lineWrapping) {
4446
+ let guess = offset + Math.round(Math.max(0, Math.min(1, (height - top) / this.height)) * this.length);
4447
+ let line = oracle.doc.lineAt(guess), lineHeight = perLine + line.length * perChar;
4448
+ let lineTop = Math.max(top, height - lineHeight / 2);
4449
+ return new BlockInfo(line.from, line.length, lineTop, lineHeight, exports.BlockType.Text);
4450
+ }
4451
+ else {
4452
+ let line = Math.max(0, Math.min(lastLine - firstLine, Math.floor((height - top) / perLine)));
4453
+ let { from, length } = oracle.doc.line(firstLine + line);
4454
+ return new BlockInfo(from, length, top + perLine * line, perLine, exports.BlockType.Text);
4455
+ }
4412
4456
  }
4413
- lineAt(value, type, doc, top, offset) {
4457
+ lineAt(value, type, oracle, top, offset) {
4414
4458
  if (type == QueryType.ByHeight)
4415
- return this.blockAt(value, doc, top, offset);
4459
+ return this.blockAt(value, oracle, top, offset);
4416
4460
  if (type == QueryType.ByPosNoHeight) {
4417
- let { from, to } = doc.lineAt(value);
4461
+ let { from, to } = oracle.doc.lineAt(value);
4418
4462
  return new BlockInfo(from, to - from, 0, 0, exports.BlockType.Text);
4419
4463
  }
4420
- let { firstLine, lineHeight } = this.lines(doc, offset);
4421
- let { from, length, number } = doc.lineAt(value);
4422
- return new BlockInfo(from, length, top + lineHeight * (number - firstLine), lineHeight, exports.BlockType.Text);
4423
- }
4424
- forEachLine(from, to, doc, top, offset, f) {
4425
- let { firstLine, lineHeight } = this.lines(doc, offset);
4426
- for (let pos = Math.max(from, offset), end = Math.min(offset + this.length, to); pos <= end;) {
4427
- let line = doc.lineAt(pos);
4428
- if (pos == from)
4429
- top += lineHeight * (line.number - firstLine);
4430
- f(new BlockInfo(line.from, line.length, top, lineHeight, exports.BlockType.Text));
4431
- top += lineHeight;
4464
+ let { firstLine, perLine, perChar } = this.heightMetrics(oracle, offset);
4465
+ let line = oracle.doc.lineAt(value), lineHeight = perLine + line.length * perChar;
4466
+ let linesAbove = line.number - firstLine;
4467
+ let lineTop = top + perLine * linesAbove + perChar * (line.from - offset - linesAbove);
4468
+ return new BlockInfo(line.from, line.length, Math.max(top, Math.min(lineTop, top + this.height - lineHeight)), lineHeight, exports.BlockType.Text);
4469
+ }
4470
+ forEachLine(from, to, oracle, top, offset, f) {
4471
+ from = Math.max(from, offset);
4472
+ to = Math.min(to, offset + this.length);
4473
+ let { firstLine, perLine, perChar } = this.heightMetrics(oracle, offset);
4474
+ for (let pos = from, lineTop = top; pos <= to;) {
4475
+ let line = oracle.doc.lineAt(pos);
4476
+ if (pos == from) {
4477
+ let linesAbove = line.number - firstLine;
4478
+ lineTop += perLine * linesAbove + perChar * (from - offset - linesAbove);
4479
+ }
4480
+ let lineHeight = perLine + perChar * line.length;
4481
+ f(new BlockInfo(line.from, line.length, lineTop, lineHeight, exports.BlockType.Text));
4482
+ lineTop += lineHeight;
4432
4483
  pos = line.to + 1;
4433
4484
  }
4434
4485
  }
@@ -4464,7 +4515,6 @@ class HeightMapGap extends HeightMap {
4464
4515
  // they would already have been added to the heightmap (gaps
4465
4516
  // only contain plain text).
4466
4517
  let nodes = [], pos = Math.max(offset, measured.from), singleHeight = -1;
4467
- let wasChanged = oracle.heightChanged;
4468
4518
  if (measured.from > offset)
4469
4519
  nodes.push(new HeightMapGap(measured.from - offset - 1).updateHeight(oracle, offset));
4470
4520
  while (pos <= end && measured.more) {
@@ -4484,8 +4534,9 @@ class HeightMapGap extends HeightMap {
4484
4534
  if (pos <= end)
4485
4535
  nodes.push(null, new HeightMapGap(end - pos).updateHeight(oracle, pos));
4486
4536
  let result = HeightMap.of(nodes);
4487
- oracle.heightChanged = wasChanged || singleHeight < 0 || Math.abs(result.height - this.height) >= Epsilon ||
4488
- Math.abs(singleHeight - this.lines(oracle.doc, offset).lineHeight) >= Epsilon;
4537
+ if (singleHeight < 0 || Math.abs(result.height - this.height) >= Epsilon ||
4538
+ Math.abs(singleHeight - this.heightMetrics(oracle, offset).perLine) >= Epsilon)
4539
+ oracle.heightChanged = true;
4489
4540
  return result;
4490
4541
  }
4491
4542
  else if (force || this.outdated) {
@@ -4504,40 +4555,40 @@ class HeightMapBranch extends HeightMap {
4504
4555
  this.size = left.size + right.size;
4505
4556
  }
4506
4557
  get break() { return this.flags & 1 /* Flag.Break */; }
4507
- blockAt(height, doc, top, offset) {
4558
+ blockAt(height, oracle, top, offset) {
4508
4559
  let mid = top + this.left.height;
4509
- return height < mid ? this.left.blockAt(height, doc, top, offset)
4510
- : this.right.blockAt(height, doc, mid, offset + this.left.length + this.break);
4560
+ return height < mid ? this.left.blockAt(height, oracle, top, offset)
4561
+ : this.right.blockAt(height, oracle, mid, offset + this.left.length + this.break);
4511
4562
  }
4512
- lineAt(value, type, doc, top, offset) {
4563
+ lineAt(value, type, oracle, top, offset) {
4513
4564
  let rightTop = top + this.left.height, rightOffset = offset + this.left.length + this.break;
4514
4565
  let left = type == QueryType.ByHeight ? value < rightTop : value < rightOffset;
4515
- let base = left ? this.left.lineAt(value, type, doc, top, offset)
4516
- : this.right.lineAt(value, type, doc, rightTop, rightOffset);
4566
+ let base = left ? this.left.lineAt(value, type, oracle, top, offset)
4567
+ : this.right.lineAt(value, type, oracle, rightTop, rightOffset);
4517
4568
  if (this.break || (left ? base.to < rightOffset : base.from > rightOffset))
4518
4569
  return base;
4519
4570
  let subQuery = type == QueryType.ByPosNoHeight ? QueryType.ByPosNoHeight : QueryType.ByPos;
4520
4571
  if (left)
4521
- return base.join(this.right.lineAt(rightOffset, subQuery, doc, rightTop, rightOffset));
4572
+ return base.join(this.right.lineAt(rightOffset, subQuery, oracle, rightTop, rightOffset));
4522
4573
  else
4523
- return this.left.lineAt(rightOffset, subQuery, doc, top, offset).join(base);
4574
+ return this.left.lineAt(rightOffset, subQuery, oracle, top, offset).join(base);
4524
4575
  }
4525
- forEachLine(from, to, doc, top, offset, f) {
4576
+ forEachLine(from, to, oracle, top, offset, f) {
4526
4577
  let rightTop = top + this.left.height, rightOffset = offset + this.left.length + this.break;
4527
4578
  if (this.break) {
4528
4579
  if (from < rightOffset)
4529
- this.left.forEachLine(from, to, doc, top, offset, f);
4580
+ this.left.forEachLine(from, to, oracle, top, offset, f);
4530
4581
  if (to >= rightOffset)
4531
- this.right.forEachLine(from, to, doc, rightTop, rightOffset, f);
4582
+ this.right.forEachLine(from, to, oracle, rightTop, rightOffset, f);
4532
4583
  }
4533
4584
  else {
4534
- let mid = this.lineAt(rightOffset, QueryType.ByPos, doc, top, offset);
4585
+ let mid = this.lineAt(rightOffset, QueryType.ByPos, oracle, top, offset);
4535
4586
  if (from < mid.from)
4536
- this.left.forEachLine(from, mid.from - 1, doc, top, offset, f);
4587
+ this.left.forEachLine(from, mid.from - 1, oracle, top, offset, f);
4537
4588
  if (mid.to >= from && mid.from <= to)
4538
4589
  f(mid);
4539
4590
  if (to > mid.to)
4540
- this.right.forEachLine(mid.to + 1, to, doc, rightTop, rightOffset, f);
4591
+ this.right.forEachLine(mid.to + 1, to, oracle, rightTop, rightOffset, f);
4541
4592
  }
4542
4593
  }
4543
4594
  replace(from, to, nodes) {
@@ -4887,11 +4938,11 @@ class ViewState {
4887
4938
  }
4888
4939
  this.viewports = viewports.sort((a, b) => a.from - b.from);
4889
4940
  this.scaler = this.heightMap.height <= 7000000 /* VP.MaxDOMHeight */ ? IdScaler :
4890
- new BigScaler(this.heightOracle.doc, this.heightMap, this.viewports);
4941
+ new BigScaler(this.heightOracle, this.heightMap, this.viewports);
4891
4942
  }
4892
4943
  updateViewportLines() {
4893
4944
  this.viewportLines = [];
4894
- this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.state.doc, 0, 0, block => {
4945
+ this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.heightOracle.setDoc(this.state.doc), 0, 0, block => {
4895
4946
  this.viewportLines.push(this.scaler.scale == 1 ? block : scaleBlock(block, this.scaler));
4896
4947
  });
4897
4948
  }
@@ -4931,8 +4982,9 @@ class ViewState {
4931
4982
  let whiteSpace = style.whiteSpace;
4932
4983
  this.defaultTextDirection = style.direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR;
4933
4984
  let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace);
4934
- let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
4935
- this.contentDOMHeight = dom.clientHeight;
4985
+ let domRect = dom.getBoundingClientRect();
4986
+ let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != domRect.height;
4987
+ this.contentDOMHeight = domRect.height;
4936
4988
  this.mustMeasureContent = false;
4937
4989
  let result = 0, bias = 0;
4938
4990
  // Vertical padding
@@ -4960,9 +5012,9 @@ class ViewState {
4960
5012
  }
4961
5013
  if (!this.inView && !this.scrollTarget)
4962
5014
  return 0;
4963
- let contentWidth = dom.clientWidth;
5015
+ let contentWidth = domRect.width;
4964
5016
  if (this.contentDOMWidth != contentWidth || this.editorHeight != view.scrollDOM.clientHeight) {
4965
- this.contentDOMWidth = contentWidth;
5017
+ this.contentDOMWidth = domRect.width;
4966
5018
  this.editorHeight = view.scrollDOM.clientHeight;
4967
5019
  result |= 8 /* UpdateFlag.Geometry */;
4968
5020
  }
@@ -4991,7 +5043,8 @@ class ViewState {
4991
5043
  result |= 2 /* UpdateFlag.Height */;
4992
5044
  }
4993
5045
  let viewportChange = !this.viewportIsAppropriate(this.viewport, bias) ||
4994
- this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to);
5046
+ this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from ||
5047
+ this.scrollTarget.range.head > this.viewport.to);
4995
5048
  if (viewportChange)
4996
5049
  this.viewport = this.getViewport(bias, this.scrollTarget);
4997
5050
  this.updateForViewport();
@@ -5017,36 +5070,37 @@ class ViewState {
5017
5070
  // bottom, depending on the bias (the change in viewport position
5018
5071
  // since the last update). It'll hold a number between 0 and 1
5019
5072
  let marginTop = 0.5 - Math.max(-0.5, Math.min(0.5, bias / 1000 /* VP.Margin */ / 2));
5020
- let map = this.heightMap, doc = this.state.doc, { visibleTop, visibleBottom } = this;
5021
- let viewport = new Viewport(map.lineAt(visibleTop - marginTop * 1000 /* VP.Margin */, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(visibleBottom + (1 - marginTop) * 1000 /* VP.Margin */, QueryType.ByHeight, doc, 0, 0).to);
5073
+ let map = this.heightMap, oracle = this.heightOracle;
5074
+ let { visibleTop, visibleBottom } = this;
5075
+ let viewport = new Viewport(map.lineAt(visibleTop - marginTop * 1000 /* VP.Margin */, QueryType.ByHeight, oracle, 0, 0).from, map.lineAt(visibleBottom + (1 - marginTop) * 1000 /* VP.Margin */, QueryType.ByHeight, oracle, 0, 0).to);
5022
5076
  // If scrollTarget is given, make sure the viewport includes that position
5023
5077
  if (scrollTarget) {
5024
5078
  let { head } = scrollTarget.range;
5025
5079
  if (head < viewport.from || head > viewport.to) {
5026
5080
  let viewHeight = Math.min(this.editorHeight, this.pixelViewport.bottom - this.pixelViewport.top);
5027
- let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
5081
+ let block = map.lineAt(head, QueryType.ByPos, oracle, 0, 0), topPos;
5028
5082
  if (scrollTarget.y == "center")
5029
5083
  topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
5030
5084
  else if (scrollTarget.y == "start" || scrollTarget.y == "nearest" && head < viewport.from)
5031
5085
  topPos = block.top;
5032
5086
  else
5033
5087
  topPos = block.bottom - viewHeight;
5034
- viewport = new Viewport(map.lineAt(topPos - 1000 /* VP.Margin */ / 2, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(topPos + viewHeight + 1000 /* VP.Margin */ / 2, QueryType.ByHeight, doc, 0, 0).to);
5088
+ viewport = new Viewport(map.lineAt(topPos - 1000 /* VP.Margin */ / 2, QueryType.ByHeight, oracle, 0, 0).from, map.lineAt(topPos + viewHeight + 1000 /* VP.Margin */ / 2, QueryType.ByHeight, oracle, 0, 0).to);
5035
5089
  }
5036
5090
  }
5037
5091
  return viewport;
5038
5092
  }
5039
5093
  mapViewport(viewport, changes) {
5040
5094
  let from = changes.mapPos(viewport.from, -1), to = changes.mapPos(viewport.to, 1);
5041
- return new Viewport(this.heightMap.lineAt(from, QueryType.ByPos, this.state.doc, 0, 0).from, this.heightMap.lineAt(to, QueryType.ByPos, this.state.doc, 0, 0).to);
5095
+ return new Viewport(this.heightMap.lineAt(from, QueryType.ByPos, this.heightOracle, 0, 0).from, this.heightMap.lineAt(to, QueryType.ByPos, this.heightOracle, 0, 0).to);
5042
5096
  }
5043
5097
  // Checks if a given viewport covers the visible part of the
5044
5098
  // document and not too much beyond that.
5045
5099
  viewportIsAppropriate({ from, to }, bias = 0) {
5046
5100
  if (!this.inView)
5047
5101
  return true;
5048
- let { top } = this.heightMap.lineAt(from, QueryType.ByPos, this.state.doc, 0, 0);
5049
- let { bottom } = this.heightMap.lineAt(to, QueryType.ByPos, this.state.doc, 0, 0);
5102
+ let { top } = this.heightMap.lineAt(from, QueryType.ByPos, this.heightOracle, 0, 0);
5103
+ let { bottom } = this.heightMap.lineAt(to, QueryType.ByPos, this.heightOracle, 0, 0);
5050
5104
  let { visibleTop, visibleBottom } = this;
5051
5105
  return (from == 0 || top <= visibleTop - Math.max(10 /* VP.MinCoverMargin */, Math.min(-bias, 250 /* VP.MaxCoverMargin */))) &&
5052
5106
  (to == this.state.doc.length ||
@@ -5183,13 +5237,13 @@ class ViewState {
5183
5237
  }
5184
5238
  lineBlockAt(pos) {
5185
5239
  return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to >= pos)) ||
5186
- scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.state.doc, 0, 0), this.scaler);
5240
+ scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.heightOracle, 0, 0), this.scaler);
5187
5241
  }
5188
5242
  lineBlockAtHeight(height) {
5189
- return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.state.doc, 0, 0), this.scaler);
5243
+ return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
5190
5244
  }
5191
5245
  elementAtHeight(height) {
5192
- return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.state.doc, 0, 0), this.scaler);
5246
+ return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.heightOracle, 0, 0), this.scaler);
5193
5247
  }
5194
5248
  get docHeight() {
5195
5249
  return this.scaler.toDOM(this.heightMap.height);
@@ -5263,11 +5317,11 @@ const IdScaler = {
5263
5317
  // regions outside the viewports so that the total height is
5264
5318
  // VP.MaxDOMHeight.
5265
5319
  class BigScaler {
5266
- constructor(doc, heightMap, viewports) {
5320
+ constructor(oracle, heightMap, viewports) {
5267
5321
  let vpHeight = 0, base = 0, domBase = 0;
5268
5322
  this.viewports = viewports.map(({ from, to }) => {
5269
- let top = heightMap.lineAt(from, QueryType.ByPos, doc, 0, 0).top;
5270
- let bottom = heightMap.lineAt(to, QueryType.ByPos, doc, 0, 0).bottom;
5323
+ let top = heightMap.lineAt(from, QueryType.ByPos, oracle, 0, 0).top;
5324
+ let bottom = heightMap.lineAt(to, QueryType.ByPos, oracle, 0, 0).bottom;
5271
5325
  vpHeight += bottom - top;
5272
5326
  return { from, to, top, bottom, domTop: 0, domBottom: 0 };
5273
5327
  });
@@ -5634,7 +5688,7 @@ function applyDOMChange(view, domChange) {
5634
5688
  };
5635
5689
  }
5636
5690
  else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
5637
- /^\. ?$/.test(change.insert.toString())) {
5691
+ /^\. ?$/.test(change.insert.toString()) && view.contentDOM.getAttribute("autocorrect") == "off") {
5638
5692
  // Detect insert-period-on-double-space Mac and Android behavior,
5639
5693
  // and transform it into a regular space insert.
5640
5694
  if (newSel && change.insert.length == 2)
@@ -6388,6 +6442,20 @@ class EditorView {
6388
6442
  this.viewState.state = state$1;
6389
6443
  return;
6390
6444
  }
6445
+ let focus = this.hasFocus, focusFlag = 0, dispatchFocus = null;
6446
+ if (transactions.some(tr => tr.annotation(isFocusChange))) {
6447
+ this.inputState.notifiedFocused = focus;
6448
+ // If a focus-change transaction is being dispatched, set this update flag.
6449
+ focusFlag = 1 /* UpdateFlag.Focus */;
6450
+ }
6451
+ else if (focus != this.inputState.notifiedFocused) {
6452
+ this.inputState.notifiedFocused = focus;
6453
+ // Schedule a separate focus transaction if necessary, otherwise
6454
+ // add a flag to this update
6455
+ dispatchFocus = focusChangeTransaction(state$1, focus);
6456
+ if (!dispatchFocus)
6457
+ focusFlag = 1 /* UpdateFlag.Focus */;
6458
+ }
6391
6459
  // If there was a pending DOM change, eagerly read it and try to
6392
6460
  // apply it after the given transactions.
6393
6461
  let pendingKey = this.observer.delayedAndroidKey, domChange = null;
@@ -6406,6 +6474,7 @@ class EditorView {
6406
6474
  if (state$1.facet(state.EditorState.phrases) != this.state.facet(state.EditorState.phrases))
6407
6475
  return this.setState(state$1);
6408
6476
  update = ViewUpdate.create(this, state$1, transactions);
6477
+ update.flags |= focusFlag;
6409
6478
  let scrollTarget = this.viewState.scrollTarget;
6410
6479
  try {
6411
6480
  this.updateState = 2 /* UpdateState.Updating */;
@@ -6443,10 +6512,15 @@ class EditorView {
6443
6512
  if (!update.empty)
6444
6513
  for (let listener of this.state.facet(updateListener))
6445
6514
  listener(update);
6446
- if (domChange) {
6447
- if (!applyDOMChange(this, domChange) && pendingKey.force)
6448
- dispatchKey(this.contentDOM, pendingKey.key, pendingKey.keyCode);
6449
- }
6515
+ if (dispatchFocus || domChange)
6516
+ Promise.resolve().then(() => {
6517
+ if (dispatchFocus && this.state == dispatchFocus.startState)
6518
+ this.dispatch(dispatchFocus);
6519
+ if (domChange) {
6520
+ if (!applyDOMChange(this, domChange) && pendingKey.force)
6521
+ dispatchKey(this.contentDOM, pendingKey.key, pendingKey.keyCode);
6522
+ }
6523
+ });
6450
6524
  }
6451
6525
  /**
6452
6526
  Reset the view to the given state. (This will cause the entire
@@ -7048,6 +7122,11 @@ called and the default behavior is prevented.
7048
7122
  */
7049
7123
  EditorView.inputHandler = inputHandler;
7050
7124
  /**
7125
+ This facet can be used to provide functions that create effects
7126
+ to be dispatched when the editor's focus state changes.
7127
+ */
7128
+ EditorView.focusChangeEffect = focusChangeEffect;
7129
+ /**
7051
7130
  By default, the editor assumes all its content has the same
7052
7131
  [text direction](https://codemirror.net/6/docs/ref/#view.Direction). Configure this with a `true`
7053
7132
  value to make it read the text direction of every (rendered)
package/dist/index.d.ts CHANGED
@@ -995,6 +995,11 @@ declare class EditorView {
995
995
  */
996
996
  static inputHandler: Facet<(view: EditorView, from: number, to: number, text: string) => boolean, readonly ((view: EditorView, from: number, to: number, text: string) => boolean)[]>;
997
997
  /**
998
+ This facet can be used to provide functions that create effects
999
+ to be dispatched when the editor's focus state changes.
1000
+ */
1001
+ static focusChangeEffect: Facet<(state: EditorState, focusing: boolean) => StateEffect<any> | null, readonly ((state: EditorState, focusing: boolean) => StateEffect<any> | null)[]>;
1002
+ /**
998
1003
  By default, the editor assumes all its content has the same
999
1004
  [text direction](https://codemirror.net/6/docs/ref/#view.Direction). Configure this with a `true`
1000
1005
  value to make it read the text direction of every (rendered)
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Text, RangeSet, MapMode, RangeValue, Facet, StateEffect, ChangeSet, findClusterBreak, EditorSelection, EditorState, findColumn, CharCategory, Transaction, Prec, codePointAt, codePointSize, combineConfig, StateField, RangeSetBuilder, countColumn } from '@codemirror/state';
1
+ import { Text, RangeSet, MapMode, RangeValue, Facet, StateEffect, ChangeSet, findClusterBreak, EditorSelection, EditorState, findColumn, CharCategory, Annotation, Transaction, Prec, codePointAt, codePointSize, combineConfig, StateField, RangeSetBuilder, countColumn } from '@codemirror/state';
2
2
  import { StyleModule } from 'style-mod';
3
3
  import { keyName, base, shift } from 'w3c-keyname';
4
4
 
@@ -515,6 +515,7 @@ class ContentView {
515
515
  }
516
516
  static get(node) { return node.cmView; }
517
517
  get isEditable() { return true; }
518
+ get isWidget() { return false; }
518
519
  merge(from, to, source, hasStart, openStart, openEnd) {
519
520
  return false;
520
521
  }
@@ -889,6 +890,7 @@ class WidgetView extends ContentView {
889
890
  return this.length ? rect : flattenRect(rect, this.side > 0);
890
891
  }
891
892
  get isEditable() { return false; }
893
+ get isWidget() { return true; }
892
894
  destroy() {
893
895
  super.destroy();
894
896
  if (this.dom)
@@ -1598,6 +1600,8 @@ class BlockWidgetView extends ContentView {
1598
1600
  }
1599
1601
  ignoreMutation() { return true; }
1600
1602
  ignoreEvent(event) { return this.widget.ignoreEvent(event); }
1603
+ get isEditable() { return false; }
1604
+ get isWidget() { return true; }
1601
1605
  destroy() {
1602
1606
  super.destroy();
1603
1607
  if (this.dom)
@@ -1779,6 +1783,7 @@ const mouseSelectionStyle = /*@__PURE__*/Facet.define();
1779
1783
  const exceptionSink = /*@__PURE__*/Facet.define();
1780
1784
  const updateListener = /*@__PURE__*/Facet.define();
1781
1785
  const inputHandler = /*@__PURE__*/Facet.define();
1786
+ const focusChangeEffect = /*@__PURE__*/Facet.define();
1782
1787
  const perLineTextDirection = /*@__PURE__*/Facet.define({
1783
1788
  combine: values => values.some(x => x)
1784
1789
  });
@@ -2021,11 +2026,6 @@ class ViewUpdate {
2021
2026
  let changedRanges = [];
2022
2027
  this.changes.iterChangedRanges((fromA, toA, fromB, toB) => changedRanges.push(new ChangedRange(fromA, toA, fromB, toB)));
2023
2028
  this.changedRanges = changedRanges;
2024
- let focus = view.hasFocus;
2025
- if (focus != view.inputState.notifiedFocused) {
2026
- view.inputState.notifiedFocused = focus;
2027
- this.flags |= 1 /* UpdateFlag.Focus */;
2028
- }
2029
2029
  }
2030
2030
  /**
2031
2031
  @internal
@@ -3160,11 +3160,11 @@ function domPosInText(node, x, y) {
3160
3160
  }
3161
3161
  return { node, offset: closestOffset > -1 ? closestOffset : generalSide > 0 ? node.nodeValue.length : 0 };
3162
3162
  }
3163
- function posAtCoords(view, { x, y }, precise, bias = -1) {
3163
+ function posAtCoords(view, coords, precise, bias = -1) {
3164
3164
  var _a;
3165
3165
  let content = view.contentDOM.getBoundingClientRect(), docTop = content.top + view.viewState.paddingTop;
3166
3166
  let block, { docHeight } = view.viewState;
3167
- let yOffset = y - docTop;
3167
+ let { x, y } = coords, yOffset = y - docTop;
3168
3168
  if (yOffset < 0)
3169
3169
  return 0;
3170
3170
  if (yOffset > docHeight)
@@ -3235,7 +3235,17 @@ function posAtCoords(view, { x, y }, precise, bias = -1) {
3235
3235
  return yOffset > block.top + block.height / 2 ? block.to : block.from;
3236
3236
  ({ node, offset } = domPosAtCoords(line.dom, x, y));
3237
3237
  }
3238
- return view.docView.posFromDOM(node, offset);
3238
+ let nearest = view.docView.nearest(node);
3239
+ if (!nearest)
3240
+ return null;
3241
+ if (nearest.isWidget) {
3242
+ let rect = nearest.dom.getBoundingClientRect();
3243
+ return coords.y < rect.top || coords.y <= rect.bottom && coords.x <= (rect.left + rect.right) / 2
3244
+ ? nearest.posAtStart : nearest.posAtEnd;
3245
+ }
3246
+ else {
3247
+ return nearest.localPosFromDOM(node, offset) + nearest.posAtStart;
3248
+ }
3239
3249
  }
3240
3250
  function posAtCoordsImprecise(view, contentRect, block, x, y) {
3241
3251
  let into = Math.round((x - contentRect.left) * view.defaultCharacterWidth);
@@ -4026,10 +4036,26 @@ handlers.copy = handlers.cut = (view, event) => {
4026
4036
  userEvent: "delete.cut"
4027
4037
  });
4028
4038
  };
4039
+ const isFocusChange = /*@__PURE__*/Annotation.define();
4040
+ function focusChangeTransaction(state, focus) {
4041
+ let effects = [];
4042
+ for (let getEffect of state.facet(focusChangeEffect)) {
4043
+ let effect = getEffect(state, focus);
4044
+ if (effect)
4045
+ effects.push(effect);
4046
+ }
4047
+ return effects ? state.update({ effects, annotations: isFocusChange.of(true) }) : null;
4048
+ }
4029
4049
  function updateForFocusChange(view) {
4030
4050
  setTimeout(() => {
4031
- if (view.hasFocus != view.inputState.notifiedFocused)
4032
- view.update([]);
4051
+ let focus = view.hasFocus;
4052
+ if (focus != view.inputState.notifiedFocused) {
4053
+ let tr = focusChangeTransaction(view.state, focus);
4054
+ if (tr)
4055
+ view.dispatch(tr);
4056
+ else
4057
+ view.update([]);
4058
+ }
4033
4059
  }, 10);
4034
4060
  }
4035
4061
  handlers.focus = view => {
@@ -4111,7 +4137,7 @@ class HeightOracle {
4111
4137
  heightForGap(from, to) {
4112
4138
  let lines = this.doc.lineAt(to).number - this.doc.lineAt(from).number + 1;
4113
4139
  if (this.lineWrapping)
4114
- lines += Math.ceil(((to - from) - (lines * this.lineLength * 0.5)) / this.lineLength);
4140
+ lines += Math.max(0, Math.ceil(((to - from) - (lines * this.lineLength * 0.5)) / this.lineLength));
4115
4141
  return this.lineHeight * lines;
4116
4142
  }
4117
4143
  heightForLine(length) {
@@ -4256,11 +4282,11 @@ class HeightMap {
4256
4282
  decomposeLeft(_to, result) { result.push(this); }
4257
4283
  decomposeRight(_from, result) { result.push(this); }
4258
4284
  applyChanges(decorations, oldDoc, oracle, changes) {
4259
- let me = this;
4285
+ let me = this, doc = oracle.doc;
4260
4286
  for (let i = changes.length - 1; i >= 0; i--) {
4261
4287
  let { fromA, toA, fromB, toB } = changes[i];
4262
- let start = me.lineAt(fromA, QueryType.ByPosNoHeight, oldDoc, 0, 0);
4263
- let end = start.to >= toA ? start : me.lineAt(toA, QueryType.ByPosNoHeight, oldDoc, 0, 0);
4288
+ let start = me.lineAt(fromA, QueryType.ByPosNoHeight, oracle.setDoc(oldDoc), 0, 0);
4289
+ let end = start.to >= toA ? start : me.lineAt(toA, QueryType.ByPosNoHeight, oracle, 0, 0);
4264
4290
  toB += end.to - toA;
4265
4291
  toA = end.to;
4266
4292
  while (i > 0 && start.from <= changes[i - 1].toA) {
@@ -4268,11 +4294,11 @@ class HeightMap {
4268
4294
  fromB = changes[i - 1].fromB;
4269
4295
  i--;
4270
4296
  if (fromA < start.from)
4271
- start = me.lineAt(fromA, QueryType.ByPosNoHeight, oldDoc, 0, 0);
4297
+ start = me.lineAt(fromA, QueryType.ByPosNoHeight, oracle, 0, 0);
4272
4298
  }
4273
4299
  fromB += start.from - fromA;
4274
4300
  fromA = start.from;
4275
- let nodes = NodeBuilder.build(oracle, decorations, fromB, toB);
4301
+ let nodes = NodeBuilder.build(oracle.setDoc(doc), decorations, fromB, toB);
4276
4302
  me = me.replace(fromA, toA, nodes);
4277
4303
  }
4278
4304
  return me.updateHeight(oracle, 0);
@@ -4339,15 +4365,15 @@ class HeightMapBlock extends HeightMap {
4339
4365
  super(length, height);
4340
4366
  this.type = type;
4341
4367
  }
4342
- blockAt(_height, _doc, top, offset) {
4368
+ blockAt(_height, _oracle, top, offset) {
4343
4369
  return new BlockInfo(offset, this.length, top, this.height, this.type);
4344
4370
  }
4345
- lineAt(_value, _type, doc, top, offset) {
4346
- return this.blockAt(0, doc, top, offset);
4371
+ lineAt(_value, _type, oracle, top, offset) {
4372
+ return this.blockAt(0, oracle, top, offset);
4347
4373
  }
4348
- forEachLine(from, to, doc, top, offset, f) {
4374
+ forEachLine(from, to, oracle, top, offset, f) {
4349
4375
  if (from <= offset + this.length && to >= offset)
4350
- f(this.blockAt(0, doc, top, offset));
4376
+ f(this.blockAt(0, oracle, top, offset));
4351
4377
  }
4352
4378
  updateHeight(oracle, offset = 0, _force = false, measured) {
4353
4379
  if (measured && measured.from <= offset && measured.more)
@@ -4393,35 +4419,60 @@ class HeightMapText extends HeightMapBlock {
4393
4419
  }
4394
4420
  class HeightMapGap extends HeightMap {
4395
4421
  constructor(length) { super(length, 0); }
4396
- lines(doc, offset) {
4397
- let firstLine = doc.lineAt(offset).number, lastLine = doc.lineAt(offset + this.length).number;
4398
- return { firstLine, lastLine, lineHeight: this.height / (lastLine - firstLine + 1) };
4422
+ heightMetrics(oracle, offset) {
4423
+ let firstLine = oracle.doc.lineAt(offset).number, lastLine = oracle.doc.lineAt(offset + this.length).number;
4424
+ let lines = lastLine - firstLine + 1;
4425
+ let perLine, perChar = 0;
4426
+ if (oracle.lineWrapping) {
4427
+ let totalPerLine = Math.min(this.height, oracle.lineHeight * lines);
4428
+ perLine = totalPerLine / lines;
4429
+ perChar = (this.height - totalPerLine) / (this.length - lines - 1);
4430
+ }
4431
+ else {
4432
+ perLine = this.height / lines;
4433
+ }
4434
+ return { firstLine, lastLine, perLine, perChar };
4399
4435
  }
4400
- blockAt(height, doc, top, offset) {
4401
- let { firstLine, lastLine, lineHeight } = this.lines(doc, offset);
4402
- let line = Math.max(0, Math.min(lastLine - firstLine, Math.floor((height - top) / lineHeight)));
4403
- let { from, length } = doc.line(firstLine + line);
4404
- return new BlockInfo(from, length, top + lineHeight * line, lineHeight, BlockType.Text);
4436
+ blockAt(height, oracle, top, offset) {
4437
+ let { firstLine, lastLine, perLine, perChar } = this.heightMetrics(oracle, offset);
4438
+ if (oracle.lineWrapping) {
4439
+ let guess = offset + Math.round(Math.max(0, Math.min(1, (height - top) / this.height)) * this.length);
4440
+ let line = oracle.doc.lineAt(guess), lineHeight = perLine + line.length * perChar;
4441
+ let lineTop = Math.max(top, height - lineHeight / 2);
4442
+ return new BlockInfo(line.from, line.length, lineTop, lineHeight, BlockType.Text);
4443
+ }
4444
+ else {
4445
+ let line = Math.max(0, Math.min(lastLine - firstLine, Math.floor((height - top) / perLine)));
4446
+ let { from, length } = oracle.doc.line(firstLine + line);
4447
+ return new BlockInfo(from, length, top + perLine * line, perLine, BlockType.Text);
4448
+ }
4405
4449
  }
4406
- lineAt(value, type, doc, top, offset) {
4450
+ lineAt(value, type, oracle, top, offset) {
4407
4451
  if (type == QueryType.ByHeight)
4408
- return this.blockAt(value, doc, top, offset);
4452
+ return this.blockAt(value, oracle, top, offset);
4409
4453
  if (type == QueryType.ByPosNoHeight) {
4410
- let { from, to } = doc.lineAt(value);
4454
+ let { from, to } = oracle.doc.lineAt(value);
4411
4455
  return new BlockInfo(from, to - from, 0, 0, BlockType.Text);
4412
4456
  }
4413
- let { firstLine, lineHeight } = this.lines(doc, offset);
4414
- let { from, length, number } = doc.lineAt(value);
4415
- return new BlockInfo(from, length, top + lineHeight * (number - firstLine), lineHeight, BlockType.Text);
4416
- }
4417
- forEachLine(from, to, doc, top, offset, f) {
4418
- let { firstLine, lineHeight } = this.lines(doc, offset);
4419
- for (let pos = Math.max(from, offset), end = Math.min(offset + this.length, to); pos <= end;) {
4420
- let line = doc.lineAt(pos);
4421
- if (pos == from)
4422
- top += lineHeight * (line.number - firstLine);
4423
- f(new BlockInfo(line.from, line.length, top, lineHeight, BlockType.Text));
4424
- top += lineHeight;
4457
+ let { firstLine, perLine, perChar } = this.heightMetrics(oracle, offset);
4458
+ let line = oracle.doc.lineAt(value), lineHeight = perLine + line.length * perChar;
4459
+ let linesAbove = line.number - firstLine;
4460
+ let lineTop = top + perLine * linesAbove + perChar * (line.from - offset - linesAbove);
4461
+ return new BlockInfo(line.from, line.length, Math.max(top, Math.min(lineTop, top + this.height - lineHeight)), lineHeight, BlockType.Text);
4462
+ }
4463
+ forEachLine(from, to, oracle, top, offset, f) {
4464
+ from = Math.max(from, offset);
4465
+ to = Math.min(to, offset + this.length);
4466
+ let { firstLine, perLine, perChar } = this.heightMetrics(oracle, offset);
4467
+ for (let pos = from, lineTop = top; pos <= to;) {
4468
+ let line = oracle.doc.lineAt(pos);
4469
+ if (pos == from) {
4470
+ let linesAbove = line.number - firstLine;
4471
+ lineTop += perLine * linesAbove + perChar * (from - offset - linesAbove);
4472
+ }
4473
+ let lineHeight = perLine + perChar * line.length;
4474
+ f(new BlockInfo(line.from, line.length, lineTop, lineHeight, BlockType.Text));
4475
+ lineTop += lineHeight;
4425
4476
  pos = line.to + 1;
4426
4477
  }
4427
4478
  }
@@ -4457,7 +4508,6 @@ class HeightMapGap extends HeightMap {
4457
4508
  // they would already have been added to the heightmap (gaps
4458
4509
  // only contain plain text).
4459
4510
  let nodes = [], pos = Math.max(offset, measured.from), singleHeight = -1;
4460
- let wasChanged = oracle.heightChanged;
4461
4511
  if (measured.from > offset)
4462
4512
  nodes.push(new HeightMapGap(measured.from - offset - 1).updateHeight(oracle, offset));
4463
4513
  while (pos <= end && measured.more) {
@@ -4477,8 +4527,9 @@ class HeightMapGap extends HeightMap {
4477
4527
  if (pos <= end)
4478
4528
  nodes.push(null, new HeightMapGap(end - pos).updateHeight(oracle, pos));
4479
4529
  let result = HeightMap.of(nodes);
4480
- oracle.heightChanged = wasChanged || singleHeight < 0 || Math.abs(result.height - this.height) >= Epsilon ||
4481
- Math.abs(singleHeight - this.lines(oracle.doc, offset).lineHeight) >= Epsilon;
4530
+ if (singleHeight < 0 || Math.abs(result.height - this.height) >= Epsilon ||
4531
+ Math.abs(singleHeight - this.heightMetrics(oracle, offset).perLine) >= Epsilon)
4532
+ oracle.heightChanged = true;
4482
4533
  return result;
4483
4534
  }
4484
4535
  else if (force || this.outdated) {
@@ -4497,40 +4548,40 @@ class HeightMapBranch extends HeightMap {
4497
4548
  this.size = left.size + right.size;
4498
4549
  }
4499
4550
  get break() { return this.flags & 1 /* Flag.Break */; }
4500
- blockAt(height, doc, top, offset) {
4551
+ blockAt(height, oracle, top, offset) {
4501
4552
  let mid = top + this.left.height;
4502
- return height < mid ? this.left.blockAt(height, doc, top, offset)
4503
- : this.right.blockAt(height, doc, mid, offset + this.left.length + this.break);
4553
+ return height < mid ? this.left.blockAt(height, oracle, top, offset)
4554
+ : this.right.blockAt(height, oracle, mid, offset + this.left.length + this.break);
4504
4555
  }
4505
- lineAt(value, type, doc, top, offset) {
4556
+ lineAt(value, type, oracle, top, offset) {
4506
4557
  let rightTop = top + this.left.height, rightOffset = offset + this.left.length + this.break;
4507
4558
  let left = type == QueryType.ByHeight ? value < rightTop : value < rightOffset;
4508
- let base = left ? this.left.lineAt(value, type, doc, top, offset)
4509
- : this.right.lineAt(value, type, doc, rightTop, rightOffset);
4559
+ let base = left ? this.left.lineAt(value, type, oracle, top, offset)
4560
+ : this.right.lineAt(value, type, oracle, rightTop, rightOffset);
4510
4561
  if (this.break || (left ? base.to < rightOffset : base.from > rightOffset))
4511
4562
  return base;
4512
4563
  let subQuery = type == QueryType.ByPosNoHeight ? QueryType.ByPosNoHeight : QueryType.ByPos;
4513
4564
  if (left)
4514
- return base.join(this.right.lineAt(rightOffset, subQuery, doc, rightTop, rightOffset));
4565
+ return base.join(this.right.lineAt(rightOffset, subQuery, oracle, rightTop, rightOffset));
4515
4566
  else
4516
- return this.left.lineAt(rightOffset, subQuery, doc, top, offset).join(base);
4567
+ return this.left.lineAt(rightOffset, subQuery, oracle, top, offset).join(base);
4517
4568
  }
4518
- forEachLine(from, to, doc, top, offset, f) {
4569
+ forEachLine(from, to, oracle, top, offset, f) {
4519
4570
  let rightTop = top + this.left.height, rightOffset = offset + this.left.length + this.break;
4520
4571
  if (this.break) {
4521
4572
  if (from < rightOffset)
4522
- this.left.forEachLine(from, to, doc, top, offset, f);
4573
+ this.left.forEachLine(from, to, oracle, top, offset, f);
4523
4574
  if (to >= rightOffset)
4524
- this.right.forEachLine(from, to, doc, rightTop, rightOffset, f);
4575
+ this.right.forEachLine(from, to, oracle, rightTop, rightOffset, f);
4525
4576
  }
4526
4577
  else {
4527
- let mid = this.lineAt(rightOffset, QueryType.ByPos, doc, top, offset);
4578
+ let mid = this.lineAt(rightOffset, QueryType.ByPos, oracle, top, offset);
4528
4579
  if (from < mid.from)
4529
- this.left.forEachLine(from, mid.from - 1, doc, top, offset, f);
4580
+ this.left.forEachLine(from, mid.from - 1, oracle, top, offset, f);
4530
4581
  if (mid.to >= from && mid.from <= to)
4531
4582
  f(mid);
4532
4583
  if (to > mid.to)
4533
- this.right.forEachLine(mid.to + 1, to, doc, rightTop, rightOffset, f);
4584
+ this.right.forEachLine(mid.to + 1, to, oracle, rightTop, rightOffset, f);
4534
4585
  }
4535
4586
  }
4536
4587
  replace(from, to, nodes) {
@@ -4880,11 +4931,11 @@ class ViewState {
4880
4931
  }
4881
4932
  this.viewports = viewports.sort((a, b) => a.from - b.from);
4882
4933
  this.scaler = this.heightMap.height <= 7000000 /* VP.MaxDOMHeight */ ? IdScaler :
4883
- new BigScaler(this.heightOracle.doc, this.heightMap, this.viewports);
4934
+ new BigScaler(this.heightOracle, this.heightMap, this.viewports);
4884
4935
  }
4885
4936
  updateViewportLines() {
4886
4937
  this.viewportLines = [];
4887
- this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.state.doc, 0, 0, block => {
4938
+ this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.heightOracle.setDoc(this.state.doc), 0, 0, block => {
4888
4939
  this.viewportLines.push(this.scaler.scale == 1 ? block : scaleBlock(block, this.scaler));
4889
4940
  });
4890
4941
  }
@@ -4924,8 +4975,9 @@ class ViewState {
4924
4975
  let whiteSpace = style.whiteSpace;
4925
4976
  this.defaultTextDirection = style.direction == "rtl" ? Direction.RTL : Direction.LTR;
4926
4977
  let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace);
4927
- let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
4928
- this.contentDOMHeight = dom.clientHeight;
4978
+ let domRect = dom.getBoundingClientRect();
4979
+ let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != domRect.height;
4980
+ this.contentDOMHeight = domRect.height;
4929
4981
  this.mustMeasureContent = false;
4930
4982
  let result = 0, bias = 0;
4931
4983
  // Vertical padding
@@ -4953,9 +5005,9 @@ class ViewState {
4953
5005
  }
4954
5006
  if (!this.inView && !this.scrollTarget)
4955
5007
  return 0;
4956
- let contentWidth = dom.clientWidth;
5008
+ let contentWidth = domRect.width;
4957
5009
  if (this.contentDOMWidth != contentWidth || this.editorHeight != view.scrollDOM.clientHeight) {
4958
- this.contentDOMWidth = contentWidth;
5010
+ this.contentDOMWidth = domRect.width;
4959
5011
  this.editorHeight = view.scrollDOM.clientHeight;
4960
5012
  result |= 8 /* UpdateFlag.Geometry */;
4961
5013
  }
@@ -4984,7 +5036,8 @@ class ViewState {
4984
5036
  result |= 2 /* UpdateFlag.Height */;
4985
5037
  }
4986
5038
  let viewportChange = !this.viewportIsAppropriate(this.viewport, bias) ||
4987
- this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from || this.scrollTarget.range.head > this.viewport.to);
5039
+ this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from ||
5040
+ this.scrollTarget.range.head > this.viewport.to);
4988
5041
  if (viewportChange)
4989
5042
  this.viewport = this.getViewport(bias, this.scrollTarget);
4990
5043
  this.updateForViewport();
@@ -5010,36 +5063,37 @@ class ViewState {
5010
5063
  // bottom, depending on the bias (the change in viewport position
5011
5064
  // since the last update). It'll hold a number between 0 and 1
5012
5065
  let marginTop = 0.5 - Math.max(-0.5, Math.min(0.5, bias / 1000 /* VP.Margin */ / 2));
5013
- let map = this.heightMap, doc = this.state.doc, { visibleTop, visibleBottom } = this;
5014
- let viewport = new Viewport(map.lineAt(visibleTop - marginTop * 1000 /* VP.Margin */, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(visibleBottom + (1 - marginTop) * 1000 /* VP.Margin */, QueryType.ByHeight, doc, 0, 0).to);
5066
+ let map = this.heightMap, oracle = this.heightOracle;
5067
+ let { visibleTop, visibleBottom } = this;
5068
+ let viewport = new Viewport(map.lineAt(visibleTop - marginTop * 1000 /* VP.Margin */, QueryType.ByHeight, oracle, 0, 0).from, map.lineAt(visibleBottom + (1 - marginTop) * 1000 /* VP.Margin */, QueryType.ByHeight, oracle, 0, 0).to);
5015
5069
  // If scrollTarget is given, make sure the viewport includes that position
5016
5070
  if (scrollTarget) {
5017
5071
  let { head } = scrollTarget.range;
5018
5072
  if (head < viewport.from || head > viewport.to) {
5019
5073
  let viewHeight = Math.min(this.editorHeight, this.pixelViewport.bottom - this.pixelViewport.top);
5020
- let block = map.lineAt(head, QueryType.ByPos, doc, 0, 0), topPos;
5074
+ let block = map.lineAt(head, QueryType.ByPos, oracle, 0, 0), topPos;
5021
5075
  if (scrollTarget.y == "center")
5022
5076
  topPos = (block.top + block.bottom) / 2 - viewHeight / 2;
5023
5077
  else if (scrollTarget.y == "start" || scrollTarget.y == "nearest" && head < viewport.from)
5024
5078
  topPos = block.top;
5025
5079
  else
5026
5080
  topPos = block.bottom - viewHeight;
5027
- viewport = new Viewport(map.lineAt(topPos - 1000 /* VP.Margin */ / 2, QueryType.ByHeight, doc, 0, 0).from, map.lineAt(topPos + viewHeight + 1000 /* VP.Margin */ / 2, QueryType.ByHeight, doc, 0, 0).to);
5081
+ viewport = new Viewport(map.lineAt(topPos - 1000 /* VP.Margin */ / 2, QueryType.ByHeight, oracle, 0, 0).from, map.lineAt(topPos + viewHeight + 1000 /* VP.Margin */ / 2, QueryType.ByHeight, oracle, 0, 0).to);
5028
5082
  }
5029
5083
  }
5030
5084
  return viewport;
5031
5085
  }
5032
5086
  mapViewport(viewport, changes) {
5033
5087
  let from = changes.mapPos(viewport.from, -1), to = changes.mapPos(viewport.to, 1);
5034
- return new Viewport(this.heightMap.lineAt(from, QueryType.ByPos, this.state.doc, 0, 0).from, this.heightMap.lineAt(to, QueryType.ByPos, this.state.doc, 0, 0).to);
5088
+ return new Viewport(this.heightMap.lineAt(from, QueryType.ByPos, this.heightOracle, 0, 0).from, this.heightMap.lineAt(to, QueryType.ByPos, this.heightOracle, 0, 0).to);
5035
5089
  }
5036
5090
  // Checks if a given viewport covers the visible part of the
5037
5091
  // document and not too much beyond that.
5038
5092
  viewportIsAppropriate({ from, to }, bias = 0) {
5039
5093
  if (!this.inView)
5040
5094
  return true;
5041
- let { top } = this.heightMap.lineAt(from, QueryType.ByPos, this.state.doc, 0, 0);
5042
- let { bottom } = this.heightMap.lineAt(to, QueryType.ByPos, this.state.doc, 0, 0);
5095
+ let { top } = this.heightMap.lineAt(from, QueryType.ByPos, this.heightOracle, 0, 0);
5096
+ let { bottom } = this.heightMap.lineAt(to, QueryType.ByPos, this.heightOracle, 0, 0);
5043
5097
  let { visibleTop, visibleBottom } = this;
5044
5098
  return (from == 0 || top <= visibleTop - Math.max(10 /* VP.MinCoverMargin */, Math.min(-bias, 250 /* VP.MaxCoverMargin */))) &&
5045
5099
  (to == this.state.doc.length ||
@@ -5176,13 +5230,13 @@ class ViewState {
5176
5230
  }
5177
5231
  lineBlockAt(pos) {
5178
5232
  return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to >= pos)) ||
5179
- scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.state.doc, 0, 0), this.scaler);
5233
+ scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.heightOracle, 0, 0), this.scaler);
5180
5234
  }
5181
5235
  lineBlockAtHeight(height) {
5182
- return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.state.doc, 0, 0), this.scaler);
5236
+ return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
5183
5237
  }
5184
5238
  elementAtHeight(height) {
5185
- return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.state.doc, 0, 0), this.scaler);
5239
+ return scaleBlock(this.heightMap.blockAt(this.scaler.fromDOM(height), this.heightOracle, 0, 0), this.scaler);
5186
5240
  }
5187
5241
  get docHeight() {
5188
5242
  return this.scaler.toDOM(this.heightMap.height);
@@ -5256,11 +5310,11 @@ const IdScaler = {
5256
5310
  // regions outside the viewports so that the total height is
5257
5311
  // VP.MaxDOMHeight.
5258
5312
  class BigScaler {
5259
- constructor(doc, heightMap, viewports) {
5313
+ constructor(oracle, heightMap, viewports) {
5260
5314
  let vpHeight = 0, base = 0, domBase = 0;
5261
5315
  this.viewports = viewports.map(({ from, to }) => {
5262
- let top = heightMap.lineAt(from, QueryType.ByPos, doc, 0, 0).top;
5263
- let bottom = heightMap.lineAt(to, QueryType.ByPos, doc, 0, 0).bottom;
5316
+ let top = heightMap.lineAt(from, QueryType.ByPos, oracle, 0, 0).top;
5317
+ let bottom = heightMap.lineAt(to, QueryType.ByPos, oracle, 0, 0).bottom;
5264
5318
  vpHeight += bottom - top;
5265
5319
  return { from, to, top, bottom, domTop: 0, domBottom: 0 };
5266
5320
  });
@@ -5627,7 +5681,7 @@ function applyDOMChange(view, domChange) {
5627
5681
  };
5628
5682
  }
5629
5683
  else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
5630
- /^\. ?$/.test(change.insert.toString())) {
5684
+ /^\. ?$/.test(change.insert.toString()) && view.contentDOM.getAttribute("autocorrect") == "off") {
5631
5685
  // Detect insert-period-on-double-space Mac and Android behavior,
5632
5686
  // and transform it into a regular space insert.
5633
5687
  if (newSel && change.insert.length == 2)
@@ -6381,6 +6435,20 @@ class EditorView {
6381
6435
  this.viewState.state = state;
6382
6436
  return;
6383
6437
  }
6438
+ let focus = this.hasFocus, focusFlag = 0, dispatchFocus = null;
6439
+ if (transactions.some(tr => tr.annotation(isFocusChange))) {
6440
+ this.inputState.notifiedFocused = focus;
6441
+ // If a focus-change transaction is being dispatched, set this update flag.
6442
+ focusFlag = 1 /* UpdateFlag.Focus */;
6443
+ }
6444
+ else if (focus != this.inputState.notifiedFocused) {
6445
+ this.inputState.notifiedFocused = focus;
6446
+ // Schedule a separate focus transaction if necessary, otherwise
6447
+ // add a flag to this update
6448
+ dispatchFocus = focusChangeTransaction(state, focus);
6449
+ if (!dispatchFocus)
6450
+ focusFlag = 1 /* UpdateFlag.Focus */;
6451
+ }
6384
6452
  // If there was a pending DOM change, eagerly read it and try to
6385
6453
  // apply it after the given transactions.
6386
6454
  let pendingKey = this.observer.delayedAndroidKey, domChange = null;
@@ -6399,6 +6467,7 @@ class EditorView {
6399
6467
  if (state.facet(EditorState.phrases) != this.state.facet(EditorState.phrases))
6400
6468
  return this.setState(state);
6401
6469
  update = ViewUpdate.create(this, state, transactions);
6470
+ update.flags |= focusFlag;
6402
6471
  let scrollTarget = this.viewState.scrollTarget;
6403
6472
  try {
6404
6473
  this.updateState = 2 /* UpdateState.Updating */;
@@ -6436,10 +6505,15 @@ class EditorView {
6436
6505
  if (!update.empty)
6437
6506
  for (let listener of this.state.facet(updateListener))
6438
6507
  listener(update);
6439
- if (domChange) {
6440
- if (!applyDOMChange(this, domChange) && pendingKey.force)
6441
- dispatchKey(this.contentDOM, pendingKey.key, pendingKey.keyCode);
6442
- }
6508
+ if (dispatchFocus || domChange)
6509
+ Promise.resolve().then(() => {
6510
+ if (dispatchFocus && this.state == dispatchFocus.startState)
6511
+ this.dispatch(dispatchFocus);
6512
+ if (domChange) {
6513
+ if (!applyDOMChange(this, domChange) && pendingKey.force)
6514
+ dispatchKey(this.contentDOM, pendingKey.key, pendingKey.keyCode);
6515
+ }
6516
+ });
6443
6517
  }
6444
6518
  /**
6445
6519
  Reset the view to the given state. (This will cause the entire
@@ -7041,6 +7115,11 @@ called and the default behavior is prevented.
7041
7115
  */
7042
7116
  EditorView.inputHandler = inputHandler;
7043
7117
  /**
7118
+ This facet can be used to provide functions that create effects
7119
+ to be dispatched when the editor's focus state changes.
7120
+ */
7121
+ EditorView.focusChangeEffect = focusChangeEffect;
7122
+ /**
7044
7123
  By default, the editor assumes all its content has the same
7045
7124
  [text direction](https://codemirror.net/6/docs/ref/#view.Direction). Configure this with a `true`
7046
7125
  value to make it read the text direction of every (rendered)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.8.1",
3
+ "version": "6.9.1",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",