@codemirror/view 6.43.11 → 6.43.13

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.43.13 (2026-09-22)
2
+
3
+ ### Bug fixes
4
+
5
+ Make `coordsAtPos` (and thus also the drawn cursor) return the outside of the line when the queried side points out of the line and there is non-dominant-direction text at that side.
6
+
7
+ `visualLineSide`, which is now deprecated because it doesn't do anything non-trivial, now just returns the logical line side, since that is actually the more correct thing to do when moving to the start or end of line.
8
+
9
+ `moveVisually` now uses positions at the logical start/end of the line to represent the first/last position in the line when the text at that side isn't int he line's dominant text direction.
10
+
11
+ ## 6.43.12 (2026-09-15)
12
+
13
+ ### Bug fixes
14
+
15
+ Make `visualLineSide` use bidi information when looking for the start or end of line with no wrap points.
16
+
17
+ Fix a bug in updating the DOM for marks around an active composition that could sometimes try to use the same DOM node twice.
18
+
19
+ Work around an issue on Firefox where a composition might be started outside of a line container.
20
+
21
+ Make sure repeated screen reader announcement messages don't get swallowed by VoiceOver.
22
+
1
23
  ## 6.43.11 (2026-09-03)
2
24
 
3
25
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -1273,10 +1273,12 @@ let movedOver = "";
1273
1273
  // This implementation moves strictly visually, without concern for a
1274
1274
  // traversal visiting every logical position in the string. It will
1275
1275
  // still do so for simple input, but situations like multiple isolates
1276
- // with the same level next to each other, or text going against the
1277
- // main dir at the end of the line, will make some positions
1276
+ // with the same level next to each other will make some positions
1278
1277
  // unreachable with this motion. Each visible cursor position will
1279
- // correspond to the lower-level bidi span that touches it.
1278
+ // correspond to the lower-level bidi span that touches it, except
1279
+ // positions at start/end of line, when the text there isn't in the
1280
+ // dominant direction, which will use a cursor at the start/end with
1281
+ // an an assoc pointing out of the line instead.
1280
1282
  //
1281
1283
  // The alternative would be to solve an order globally for a given
1282
1284
  // line, making sure that it includes every position, but that would
@@ -1285,8 +1287,22 @@ let movedOver = "";
1285
1287
  // people. (And would generally be a lot more complicated.)
1286
1288
  function moveVisually(line, order, dir, start, forward) {
1287
1289
  var _a;
1288
- let startIndex = start.head - line.from;
1289
- let spanI = BidiSpan.find(order, startIndex, (_a = start.bidiLevel) !== null && _a !== void 0 ? _a : -1, start.assoc);
1290
+ if (!line.length)
1291
+ return null;
1292
+ let startIndex = start.head - line.from, spanI;
1293
+ if (start.head == line.from && start.assoc < 0) { // Start of line.
1294
+ if (!forward)
1295
+ return null;
1296
+ startIndex = order[spanI = 0].side(false, dir);
1297
+ }
1298
+ else if (start.head == line.to && start.assoc > 0) { // End of line
1299
+ if (forward)
1300
+ return null;
1301
+ startIndex = order[spanI = order.length - 1].side(true, dir);
1302
+ }
1303
+ else {
1304
+ spanI = BidiSpan.find(order, startIndex, (_a = start.bidiLevel) !== null && _a !== void 0 ? _a : -1, start.assoc);
1305
+ }
1290
1306
  let span = order[spanI], spanEnd = span.side(forward, dir);
1291
1307
  // End of span
1292
1308
  if (startIndex == spanEnd) {
@@ -1302,8 +1318,13 @@ function moveVisually(line, order, dir, start, forward) {
1302
1318
  nextIndex = spanEnd;
1303
1319
  movedOver = line.text.slice(Math.min(startIndex, nextIndex), Math.max(startIndex, nextIndex));
1304
1320
  let nextSpan = spanI == (forward ? order.length - 1 : 0) ? null : order[spanI + (forward ? 1 : -1)];
1305
- if (nextSpan && nextIndex == spanEnd && nextSpan.level + (forward ? 0 : 1) < span.level)
1306
- return state.EditorSelection.cursor(nextSpan.side(!forward, dir) + line.from, nextSpan.forward(forward, dir) ? 1 : -1, nextSpan.level);
1321
+ if (nextIndex == spanEnd) {
1322
+ // End of line
1323
+ if (!nextSpan)
1324
+ return forward ? state.EditorSelection.cursor(line.to, 1) : state.EditorSelection.cursor(line.from, -1);
1325
+ if (nextSpan.level + (forward ? 0 : 1) < span.level)
1326
+ return state.EditorSelection.cursor(nextSpan.side(!forward, dir) + line.from, nextSpan.forward(forward, dir) ? 1 : -1, nextSpan.level);
1327
+ }
1307
1328
  return state.EditorSelection.cursor(nextIndex + line.from, span.forward(forward, dir) ? -1 : 1, span.level);
1308
1329
  }
1309
1330
  function autoDirection(text, from, to) {
@@ -2323,12 +2344,13 @@ class TileBuilder {
2323
2344
  head = last;
2324
2345
  }
2325
2346
  else {
2347
+ let { dom } = mark;
2326
2348
  if (this.cache.reused.get(mark)) {
2327
2349
  let tile = Tile.get(mark.dom);
2328
2350
  if (tile)
2329
- tile.setDOM(freeNode(mark.dom));
2351
+ dom = freeNode(mark.dom);
2330
2352
  }
2331
- let nw = MarkTile.of(mark.mark, mark.dom);
2353
+ let nw = MarkTile.of(mark.mark, dom);
2332
2354
  head.append(nw);
2333
2355
  head = nw;
2334
2356
  }
@@ -2828,12 +2850,14 @@ class TileUpdate {
2828
2850
  else if (tile === null || tile === void 0 ? void 0 : tile.isLine())
2829
2851
  line = tile;
2830
2852
  else if (tile instanceof BlockWrapperTile) ; // Ignore
2831
- else if (parent.nodeName == "DIV" && !line && parent != this.view.contentDOM)
2853
+ else if (parent.nodeName == "DIV" && !line)
2832
2854
  line = new LineTile(parent, lineBaseAttrs);
2833
2855
  else if (!line)
2834
2856
  marks.push(MarkTile.of(new MarkDecoration({ tagName: parent.nodeName.toLowerCase(), attributes: getAttrs(parent) }), parent));
2835
2857
  }
2836
- return { line: line, marks };
2858
+ if (!line)
2859
+ return null;
2860
+ return { line, marks };
2837
2861
  }
2838
2862
  }
2839
2863
  function hasContent(tile, requireText) {
@@ -3664,18 +3688,18 @@ function blockAt(view, pos, side) {
3664
3688
  return line;
3665
3689
  }
3666
3690
  function moveToLineBoundary(view, start, forward, includeWrap) {
3667
- let line = blockAt(view, start.head, start.assoc || -1);
3668
- let coords = !includeWrap || line.type != exports.BlockType.Text || !(view.lineWrapping || line.widgetLineBreaks) ? null
3669
- : view.coordsAtPos(start.assoc < 0 && start.head > line.from ? start.head - 1 : start.head);
3691
+ let block = blockAt(view, start.head, start.assoc || -1);
3692
+ let coords = !includeWrap || block.type != exports.BlockType.Text || !(view.lineWrapping || block.widgetLineBreaks) ? null
3693
+ : view.coordsAtPos(start.assoc < 0 && start.head > block.from ? start.head - 1 : start.head);
3670
3694
  if (coords) {
3671
3695
  let editorRect = view.dom.getBoundingClientRect();
3672
- let direction = view.textDirectionAt(line.from);
3696
+ let direction = view.textDirectionAt(block.from);
3673
3697
  let pos = view.posAtCoords({ x: forward == (direction == exports.Direction.LTR) ? editorRect.right - 1 : editorRect.left + 1,
3674
3698
  y: (coords.top + coords.bottom) / 2 });
3675
3699
  if (pos != null)
3676
3700
  return state.EditorSelection.cursor(pos, forward ? -1 : 1);
3677
3701
  }
3678
- return state.EditorSelection.cursor(forward ? line.to : line.from, forward ? -1 : 1);
3702
+ return state.EditorSelection.cursor(forward ? block.to : block.from, forward ? -1 : 1);
3679
3703
  }
3680
3704
  function moveByChar(view, start, forward, by) {
3681
3705
  let line = view.state.doc.lineAt(start.head), spans = view.bidiSpans(line);
@@ -3688,7 +3712,7 @@ function moveByChar(view, start, forward, by) {
3688
3712
  char = "\n";
3689
3713
  line = view.state.doc.line(line.number + (forward ? 1 : -1));
3690
3714
  spans = view.bidiSpans(line);
3691
- next = view.visualLineSide(line, !forward);
3715
+ next = forward ? state.EditorSelection.cursor(line.from, -1) : state.EditorSelection.cursor(line.to, 1);
3692
3716
  }
3693
3717
  if (!check) {
3694
3718
  if (!by)
@@ -4476,7 +4500,9 @@ function selectionFromPoints(points, base) {
4476
4500
  if (points.length == 0)
4477
4501
  return null;
4478
4502
  let anchor = points[0].pos, head = points.length == 2 ? points[1].pos : anchor;
4479
- return anchor > -1 && head > -1 ? state.EditorSelection.single(anchor + base, head + base) : null;
4503
+ return anchor < 0 || head < 0 ? null
4504
+ : anchor == head ? state.EditorSelection.create([state.EditorSelection.cursor(head + base, -1)])
4505
+ : state.EditorSelection.single(anchor + base, head + base);
4480
4506
  }
4481
4507
  function sameSelPos(selection, range) {
4482
4508
  return range.head == selection.main.head && range.anchor == selection.main.anchor;
@@ -4628,8 +4654,9 @@ class InputState {
4628
4654
  if (mods.shiftKey && browser.ios && !/^(off|none)$/.test(this.view.contentDOM.autocapitalize) &&
4629
4655
  iosVirtualKeyboardOpen(this.view.win))
4630
4656
  mods.shiftKey = false;
4631
- this.pendingIOSKey = { key: event.key, keyCode: event.keyCode, mods };
4632
- setTimeout(() => this.flushIOSKey(), 50);
4657
+ let pending = this.pendingIOSKey = { key: event.key, keyCode: event.keyCode, mods };
4658
+ setTimeout(() => { if (this.pendingIOSKey == pending)
4659
+ this.flushIOSKey(); }, 50);
4633
4660
  return true;
4634
4661
  }
4635
4662
  if (event.keyCode != 229)
@@ -4918,14 +4945,14 @@ function doPaste(view, input) {
4918
4945
  lastLine = line.from;
4919
4946
  let insert = state$1.toText((byLine ? text.line(i++).text : input) + state$1.lineBreak);
4920
4947
  return { changes: { from: line.from, insert },
4921
- range: state.EditorSelection.cursor(range.from + insert.length) };
4948
+ range: state.EditorSelection.cursor(range.from + insert.length, -1) };
4922
4949
  });
4923
4950
  }
4924
4951
  else if (byLine) {
4925
4952
  changes = state$1.changeByRange(range => {
4926
4953
  let line = text.line(i++);
4927
4954
  return { changes: { from: range.from, to: range.to, insert: line.text },
4928
- range: state.EditorSelection.cursor(range.from + line.length) };
4955
+ range: state.EditorSelection.cursor(range.from + line.length, -1) };
4929
4956
  });
4930
4957
  }
4931
4958
  else {
@@ -5263,6 +5290,13 @@ observers.compositionstart = observers.compositionupdate = view => {
5263
5290
  if (view.inputState.compositionFirstChange == null)
5264
5291
  view.inputState.compositionFirstChange = true;
5265
5292
  if (view.inputState.composing < 0) {
5293
+ let { main } = view.state.selection;
5294
+ if (!main.empty && view.lineBlockAt(main.from).from != view.lineBlockAt(main.to).from) {
5295
+ view.dispatch({
5296
+ changes: view.state.selection.ranges.filter(r => !r.empty).map(r => ({ from: r.from, to: r.to })),
5297
+ userEvent: "input"
5298
+ });
5299
+ }
5266
5300
  // FIXME possibly set a timeout to clear it again on Android
5267
5301
  view.inputState.composing = 0;
5268
5302
  }
@@ -5615,13 +5649,13 @@ class HeightMap {
5615
5649
  after += next.size;
5616
5650
  }
5617
5651
  }
5618
- let brk = 0;
5652
+ let brk = false;
5619
5653
  if (nodes[i - 1] == null) {
5620
- brk = 1;
5654
+ brk = true;
5621
5655
  i--;
5622
5656
  }
5623
5657
  else if (nodes[i] == null) {
5624
- brk = 1;
5658
+ brk = true;
5625
5659
  j++;
5626
5660
  }
5627
5661
  return new HeightMapBranch(HeightMap.of(nodes.slice(0, i)), brk, HeightMap.of(nodes.slice(j)));
@@ -5850,12 +5884,14 @@ class HeightMapGap extends HeightMap {
5850
5884
  }
5851
5885
  class HeightMapBranch extends HeightMap {
5852
5886
  constructor(left, brk, right) {
5853
- super(left.length + brk + right.length, left.height + right.height, brk | (left.outdated || right.outdated ? 2 /* Flag.Outdated */ : 0));
5887
+ super(left.length + (brk ? 1 : 0) + right.length, left.height + right.height, (brk ? 1 /* Flag.Break */ : 0) | (left.outdated || right.outdated ? 2 /* Flag.Outdated */ : 0));
5854
5888
  this.left = left;
5855
5889
  this.right = right;
5856
5890
  this.size = left.size + right.size;
5857
5891
  }
5858
- get break() { return this.flags & 1 /* Flag.Break */; }
5892
+ // Returns 1 if there is a line break between this.left and
5893
+ // this.right, 0 otherwise.
5894
+ get break() { return (this.flags & 1 /* Flag.Break */); }
5859
5895
  blockAt(height, oracle, top, offset) {
5860
5896
  let mid = top + this.left.height;
5861
5897
  return height < mid ? this.left.blockAt(height, oracle, top, offset)
@@ -5885,11 +5921,11 @@ class HeightMapBranch extends HeightMap {
5885
5921
  else {
5886
5922
  let mid = this.lineAt(rightOffset, QueryType.ByPos, oracle, top, offset);
5887
5923
  if (from < mid.from)
5888
- this.left.forEachLine(from, mid.from - 1, oracle, top, offset, f);
5924
+ this.left.forEachLine(from, Math.min(to, mid.from - 1), oracle, top, offset, f);
5889
5925
  if (mid.to >= from && mid.from <= to)
5890
5926
  f(mid);
5891
5927
  if (to > mid.to)
5892
- this.right.forEachLine(mid.to + 1, to, oracle, rightTop, rightOffset, f);
5928
+ this.right.forEachLine(Math.max(from, mid.to + 1), to, oracle, rightTop, rightOffset, f);
5893
5929
  }
5894
5930
  }
5895
5931
  replace(from, to, nodes) {
@@ -7896,6 +7932,7 @@ class EditorView {
7896
7932
  @internal
7897
7933
  */
7898
7934
  this.measureRequests = [];
7935
+ this.clearAnnouncement = -1;
7899
7936
  this.contentDOM = document.createElement("div");
7900
7937
  this.scrollDOM = document.createElement("div");
7901
7938
  this.scrollDOM.tabIndex = -1;
@@ -8301,9 +8338,14 @@ class EditorView {
8301
8338
  for (let tr of trs)
8302
8339
  for (let effect of tr.effects)
8303
8340
  if (effect.is(EditorView.announce)) {
8304
- if (first)
8341
+ if (first) {
8305
8342
  this.announceDOM.textContent = "";
8306
- first = false;
8343
+ this.win.clearTimeout(this.clearAnnouncement);
8344
+ this.clearAnnouncement = this.win.setTimeout(() => {
8345
+ this.announceDOM.textContent = "\u00a0";
8346
+ }, 200);
8347
+ first = false;
8348
+ }
8307
8349
  let div = this.announceDOM.appendChild(document.createElement("div"));
8308
8350
  div.textContent = effect.value;
8309
8351
  }
@@ -8452,15 +8494,11 @@ class EditorView {
8452
8494
  return skipAtoms(this, start, moveByChar(this, start, forward, initial => byGroup(this, start.head, initial)));
8453
8495
  }
8454
8496
  /**
8455
- Get the cursor position visually at the start or end of a line.
8456
- Note that this may differ from the _logical_ position at its
8457
- start or end (which is simply at `line.from`/`line.to`) if text
8458
- at the start or end goes against the line's base text direction.
8497
+ **\[DEPRECATED]** Get the cursor position visually at the start
8498
+ or end of a line.
8459
8499
  */
8460
8500
  visualLineSide(line, end) {
8461
- let order = this.bidiSpans(line), dir = this.textDirectionAt(line.from);
8462
- let span = order[end ? order.length - 1 : 0];
8463
- return state.EditorSelection.cursor(span.side(end, dir) + line.from, span.forward(!end, dir) ? 1 : -1);
8501
+ return end ? state.EditorSelection.cursor(line.to, 1) : state.EditorSelection.cursor(line.from, -1);
8464
8502
  }
8465
8503
  /**
8466
8504
  Move to the next line boundary in the given direction. If
@@ -8529,6 +8567,20 @@ class EditorView {
8529
8567
  this.readMeasured();
8530
8568
  let line = this.state.doc.lineAt(pos), order = this.bidiSpans(line);
8531
8569
  let span = order[BidiSpan.find(order, pos - line.from, -1, side)];
8570
+ // For positions at start/end of line, if the side points out of
8571
+ // the line and the text there isn't in the dominant dir, return
8572
+ // the position of the outermost character on the line.
8573
+ if (line.length && (pos == line.from && side < 0 || pos == line.to && side > 0) &&
8574
+ span.dir != this.textDirectionAt(line.from)) {
8575
+ if (pos == line.to) {
8576
+ pos = line.from + span.from;
8577
+ side = 1;
8578
+ }
8579
+ else {
8580
+ pos = line.from + span.to;
8581
+ side = -1;
8582
+ }
8583
+ }
8532
8584
  return this.docView.coordsAt(pos, side, span.dir == exports.Direction.RTL);
8533
8585
  }
8534
8586
  /**
@@ -8653,6 +8705,7 @@ class EditorView {
8653
8705
  this.docView.destroy();
8654
8706
  this.dom.remove();
8655
8707
  this.observer.destroy();
8708
+ this.win.clearTimeout(this.clearAnnouncement);
8656
8709
  if (this.measureScheduled > -1)
8657
8710
  this.win.cancelAnimationFrame(this.measureScheduled);
8658
8711
  this.destroyed = true;
package/dist/index.d.cts CHANGED
@@ -813,6 +813,7 @@ declare class EditorView {
813
813
  private styleModules;
814
814
  private bidiCache;
815
815
  private destroyed;
816
+ private clearAnnouncement;
816
817
  /**
817
818
  Construct a new view. You'll want to either provide a `parent`
818
819
  option, or put `view.dom` into your document after creating a
@@ -958,10 +959,8 @@ declare class EditorView {
958
959
  */
959
960
  moveByGroup(start: SelectionRange, forward: boolean): SelectionRange;
960
961
  /**
961
- Get the cursor position visually at the start or end of a line.
962
- Note that this may differ from the _logical_ position at its
963
- start or end (which is simply at `line.from`/`line.to`) if text
964
- at the start or end goes against the line's base text direction.
962
+ **\[DEPRECATED]** Get the cursor position visually at the start
963
+ or end of a line.
965
964
  */
966
965
  visualLineSide(line: Line, end: boolean): SelectionRange;
967
966
  /**
package/dist/index.d.ts CHANGED
@@ -813,6 +813,7 @@ declare class EditorView {
813
813
  private styleModules;
814
814
  private bidiCache;
815
815
  private destroyed;
816
+ private clearAnnouncement;
816
817
  /**
817
818
  Construct a new view. You'll want to either provide a `parent`
818
819
  option, or put `view.dom` into your document after creating a
@@ -958,10 +959,8 @@ declare class EditorView {
958
959
  */
959
960
  moveByGroup(start: SelectionRange, forward: boolean): SelectionRange;
960
961
  /**
961
- Get the cursor position visually at the start or end of a line.
962
- Note that this may differ from the _logical_ position at its
963
- start or end (which is simply at `line.from`/`line.to`) if text
964
- at the start or end goes against the line's base text direction.
962
+ **\[DEPRECATED]** Get the cursor position visually at the start
963
+ or end of a line.
965
964
  */
966
965
  visualLineSide(line: Line, end: boolean): SelectionRange;
967
966
  /**
package/dist/index.js CHANGED
@@ -1269,10 +1269,12 @@ let movedOver = "";
1269
1269
  // This implementation moves strictly visually, without concern for a
1270
1270
  // traversal visiting every logical position in the string. It will
1271
1271
  // still do so for simple input, but situations like multiple isolates
1272
- // with the same level next to each other, or text going against the
1273
- // main dir at the end of the line, will make some positions
1272
+ // with the same level next to each other will make some positions
1274
1273
  // unreachable with this motion. Each visible cursor position will
1275
- // correspond to the lower-level bidi span that touches it.
1274
+ // correspond to the lower-level bidi span that touches it, except
1275
+ // positions at start/end of line, when the text there isn't in the
1276
+ // dominant direction, which will use a cursor at the start/end with
1277
+ // an an assoc pointing out of the line instead.
1276
1278
  //
1277
1279
  // The alternative would be to solve an order globally for a given
1278
1280
  // line, making sure that it includes every position, but that would
@@ -1281,8 +1283,22 @@ let movedOver = "";
1281
1283
  // people. (And would generally be a lot more complicated.)
1282
1284
  function moveVisually(line, order, dir, start, forward) {
1283
1285
  var _a;
1284
- let startIndex = start.head - line.from;
1285
- let spanI = BidiSpan.find(order, startIndex, (_a = start.bidiLevel) !== null && _a !== void 0 ? _a : -1, start.assoc);
1286
+ if (!line.length)
1287
+ return null;
1288
+ let startIndex = start.head - line.from, spanI;
1289
+ if (start.head == line.from && start.assoc < 0) { // Start of line.
1290
+ if (!forward)
1291
+ return null;
1292
+ startIndex = order[spanI = 0].side(false, dir);
1293
+ }
1294
+ else if (start.head == line.to && start.assoc > 0) { // End of line
1295
+ if (forward)
1296
+ return null;
1297
+ startIndex = order[spanI = order.length - 1].side(true, dir);
1298
+ }
1299
+ else {
1300
+ spanI = BidiSpan.find(order, startIndex, (_a = start.bidiLevel) !== null && _a !== void 0 ? _a : -1, start.assoc);
1301
+ }
1286
1302
  let span = order[spanI], spanEnd = span.side(forward, dir);
1287
1303
  // End of span
1288
1304
  if (startIndex == spanEnd) {
@@ -1298,8 +1314,13 @@ function moveVisually(line, order, dir, start, forward) {
1298
1314
  nextIndex = spanEnd;
1299
1315
  movedOver = line.text.slice(Math.min(startIndex, nextIndex), Math.max(startIndex, nextIndex));
1300
1316
  let nextSpan = spanI == (forward ? order.length - 1 : 0) ? null : order[spanI + (forward ? 1 : -1)];
1301
- if (nextSpan && nextIndex == spanEnd && nextSpan.level + (forward ? 0 : 1) < span.level)
1302
- return EditorSelection.cursor(nextSpan.side(!forward, dir) + line.from, nextSpan.forward(forward, dir) ? 1 : -1, nextSpan.level);
1317
+ if (nextIndex == spanEnd) {
1318
+ // End of line
1319
+ if (!nextSpan)
1320
+ return forward ? EditorSelection.cursor(line.to, 1) : EditorSelection.cursor(line.from, -1);
1321
+ if (nextSpan.level + (forward ? 0 : 1) < span.level)
1322
+ return EditorSelection.cursor(nextSpan.side(!forward, dir) + line.from, nextSpan.forward(forward, dir) ? 1 : -1, nextSpan.level);
1323
+ }
1303
1324
  return EditorSelection.cursor(nextIndex + line.from, span.forward(forward, dir) ? -1 : 1, span.level);
1304
1325
  }
1305
1326
  function autoDirection(text, from, to) {
@@ -2319,12 +2340,13 @@ class TileBuilder {
2319
2340
  head = last;
2320
2341
  }
2321
2342
  else {
2343
+ let { dom } = mark;
2322
2344
  if (this.cache.reused.get(mark)) {
2323
2345
  let tile = Tile.get(mark.dom);
2324
2346
  if (tile)
2325
- tile.setDOM(freeNode(mark.dom));
2347
+ dom = freeNode(mark.dom);
2326
2348
  }
2327
- let nw = MarkTile.of(mark.mark, mark.dom);
2349
+ let nw = MarkTile.of(mark.mark, dom);
2328
2350
  head.append(nw);
2329
2351
  head = nw;
2330
2352
  }
@@ -2824,12 +2846,14 @@ class TileUpdate {
2824
2846
  else if (tile === null || tile === void 0 ? void 0 : tile.isLine())
2825
2847
  line = tile;
2826
2848
  else if (tile instanceof BlockWrapperTile) ; // Ignore
2827
- else if (parent.nodeName == "DIV" && !line && parent != this.view.contentDOM)
2849
+ else if (parent.nodeName == "DIV" && !line)
2828
2850
  line = new LineTile(parent, lineBaseAttrs);
2829
2851
  else if (!line)
2830
2852
  marks.push(MarkTile.of(new MarkDecoration({ tagName: parent.nodeName.toLowerCase(), attributes: getAttrs(parent) }), parent));
2831
2853
  }
2832
- return { line: line, marks };
2854
+ if (!line)
2855
+ return null;
2856
+ return { line, marks };
2833
2857
  }
2834
2858
  }
2835
2859
  function hasContent(tile, requireText) {
@@ -3660,18 +3684,18 @@ function blockAt(view, pos, side) {
3660
3684
  return line;
3661
3685
  }
3662
3686
  function moveToLineBoundary(view, start, forward, includeWrap) {
3663
- let line = blockAt(view, start.head, start.assoc || -1);
3664
- let coords = !includeWrap || line.type != BlockType.Text || !(view.lineWrapping || line.widgetLineBreaks) ? null
3665
- : view.coordsAtPos(start.assoc < 0 && start.head > line.from ? start.head - 1 : start.head);
3687
+ let block = blockAt(view, start.head, start.assoc || -1);
3688
+ let coords = !includeWrap || block.type != BlockType.Text || !(view.lineWrapping || block.widgetLineBreaks) ? null
3689
+ : view.coordsAtPos(start.assoc < 0 && start.head > block.from ? start.head - 1 : start.head);
3666
3690
  if (coords) {
3667
3691
  let editorRect = view.dom.getBoundingClientRect();
3668
- let direction = view.textDirectionAt(line.from);
3692
+ let direction = view.textDirectionAt(block.from);
3669
3693
  let pos = view.posAtCoords({ x: forward == (direction == Direction.LTR) ? editorRect.right - 1 : editorRect.left + 1,
3670
3694
  y: (coords.top + coords.bottom) / 2 });
3671
3695
  if (pos != null)
3672
3696
  return EditorSelection.cursor(pos, forward ? -1 : 1);
3673
3697
  }
3674
- return EditorSelection.cursor(forward ? line.to : line.from, forward ? -1 : 1);
3698
+ return EditorSelection.cursor(forward ? block.to : block.from, forward ? -1 : 1);
3675
3699
  }
3676
3700
  function moveByChar(view, start, forward, by) {
3677
3701
  let line = view.state.doc.lineAt(start.head), spans = view.bidiSpans(line);
@@ -3684,7 +3708,7 @@ function moveByChar(view, start, forward, by) {
3684
3708
  char = "\n";
3685
3709
  line = view.state.doc.line(line.number + (forward ? 1 : -1));
3686
3710
  spans = view.bidiSpans(line);
3687
- next = view.visualLineSide(line, !forward);
3711
+ next = forward ? EditorSelection.cursor(line.from, -1) : EditorSelection.cursor(line.to, 1);
3688
3712
  }
3689
3713
  if (!check) {
3690
3714
  if (!by)
@@ -4472,7 +4496,9 @@ function selectionFromPoints(points, base) {
4472
4496
  if (points.length == 0)
4473
4497
  return null;
4474
4498
  let anchor = points[0].pos, head = points.length == 2 ? points[1].pos : anchor;
4475
- return anchor > -1 && head > -1 ? EditorSelection.single(anchor + base, head + base) : null;
4499
+ return anchor < 0 || head < 0 ? null
4500
+ : anchor == head ? EditorSelection.create([EditorSelection.cursor(head + base, -1)])
4501
+ : EditorSelection.single(anchor + base, head + base);
4476
4502
  }
4477
4503
  function sameSelPos(selection, range) {
4478
4504
  return range.head == selection.main.head && range.anchor == selection.main.anchor;
@@ -4624,8 +4650,9 @@ class InputState {
4624
4650
  if (mods.shiftKey && browser.ios && !/^(off|none)$/.test(this.view.contentDOM.autocapitalize) &&
4625
4651
  iosVirtualKeyboardOpen(this.view.win))
4626
4652
  mods.shiftKey = false;
4627
- this.pendingIOSKey = { key: event.key, keyCode: event.keyCode, mods };
4628
- setTimeout(() => this.flushIOSKey(), 50);
4653
+ let pending = this.pendingIOSKey = { key: event.key, keyCode: event.keyCode, mods };
4654
+ setTimeout(() => { if (this.pendingIOSKey == pending)
4655
+ this.flushIOSKey(); }, 50);
4629
4656
  return true;
4630
4657
  }
4631
4658
  if (event.keyCode != 229)
@@ -4914,14 +4941,14 @@ function doPaste(view, input) {
4914
4941
  lastLine = line.from;
4915
4942
  let insert = state.toText((byLine ? text.line(i++).text : input) + state.lineBreak);
4916
4943
  return { changes: { from: line.from, insert },
4917
- range: EditorSelection.cursor(range.from + insert.length) };
4944
+ range: EditorSelection.cursor(range.from + insert.length, -1) };
4918
4945
  });
4919
4946
  }
4920
4947
  else if (byLine) {
4921
4948
  changes = state.changeByRange(range => {
4922
4949
  let line = text.line(i++);
4923
4950
  return { changes: { from: range.from, to: range.to, insert: line.text },
4924
- range: EditorSelection.cursor(range.from + line.length) };
4951
+ range: EditorSelection.cursor(range.from + line.length, -1) };
4925
4952
  });
4926
4953
  }
4927
4954
  else {
@@ -5259,6 +5286,13 @@ observers.compositionstart = observers.compositionupdate = view => {
5259
5286
  if (view.inputState.compositionFirstChange == null)
5260
5287
  view.inputState.compositionFirstChange = true;
5261
5288
  if (view.inputState.composing < 0) {
5289
+ let { main } = view.state.selection;
5290
+ if (!main.empty && view.lineBlockAt(main.from).from != view.lineBlockAt(main.to).from) {
5291
+ view.dispatch({
5292
+ changes: view.state.selection.ranges.filter(r => !r.empty).map(r => ({ from: r.from, to: r.to })),
5293
+ userEvent: "input"
5294
+ });
5295
+ }
5262
5296
  // FIXME possibly set a timeout to clear it again on Android
5263
5297
  view.inputState.composing = 0;
5264
5298
  }
@@ -5610,13 +5644,13 @@ class HeightMap {
5610
5644
  after += next.size;
5611
5645
  }
5612
5646
  }
5613
- let brk = 0;
5647
+ let brk = false;
5614
5648
  if (nodes[i - 1] == null) {
5615
- brk = 1;
5649
+ brk = true;
5616
5650
  i--;
5617
5651
  }
5618
5652
  else if (nodes[i] == null) {
5619
- brk = 1;
5653
+ brk = true;
5620
5654
  j++;
5621
5655
  }
5622
5656
  return new HeightMapBranch(HeightMap.of(nodes.slice(0, i)), brk, HeightMap.of(nodes.slice(j)));
@@ -5845,12 +5879,14 @@ class HeightMapGap extends HeightMap {
5845
5879
  }
5846
5880
  class HeightMapBranch extends HeightMap {
5847
5881
  constructor(left, brk, right) {
5848
- super(left.length + brk + right.length, left.height + right.height, brk | (left.outdated || right.outdated ? 2 /* Flag.Outdated */ : 0));
5882
+ super(left.length + (brk ? 1 : 0) + right.length, left.height + right.height, (brk ? 1 /* Flag.Break */ : 0) | (left.outdated || right.outdated ? 2 /* Flag.Outdated */ : 0));
5849
5883
  this.left = left;
5850
5884
  this.right = right;
5851
5885
  this.size = left.size + right.size;
5852
5886
  }
5853
- get break() { return this.flags & 1 /* Flag.Break */; }
5887
+ // Returns 1 if there is a line break between this.left and
5888
+ // this.right, 0 otherwise.
5889
+ get break() { return (this.flags & 1 /* Flag.Break */); }
5854
5890
  blockAt(height, oracle, top, offset) {
5855
5891
  let mid = top + this.left.height;
5856
5892
  return height < mid ? this.left.blockAt(height, oracle, top, offset)
@@ -5880,11 +5916,11 @@ class HeightMapBranch extends HeightMap {
5880
5916
  else {
5881
5917
  let mid = this.lineAt(rightOffset, QueryType.ByPos, oracle, top, offset);
5882
5918
  if (from < mid.from)
5883
- this.left.forEachLine(from, mid.from - 1, oracle, top, offset, f);
5919
+ this.left.forEachLine(from, Math.min(to, mid.from - 1), oracle, top, offset, f);
5884
5920
  if (mid.to >= from && mid.from <= to)
5885
5921
  f(mid);
5886
5922
  if (to > mid.to)
5887
- this.right.forEachLine(mid.to + 1, to, oracle, rightTop, rightOffset, f);
5923
+ this.right.forEachLine(Math.max(from, mid.to + 1), to, oracle, rightTop, rightOffset, f);
5888
5924
  }
5889
5925
  }
5890
5926
  replace(from, to, nodes) {
@@ -7891,6 +7927,7 @@ class EditorView {
7891
7927
  @internal
7892
7928
  */
7893
7929
  this.measureRequests = [];
7930
+ this.clearAnnouncement = -1;
7894
7931
  this.contentDOM = document.createElement("div");
7895
7932
  this.scrollDOM = document.createElement("div");
7896
7933
  this.scrollDOM.tabIndex = -1;
@@ -8296,9 +8333,14 @@ class EditorView {
8296
8333
  for (let tr of trs)
8297
8334
  for (let effect of tr.effects)
8298
8335
  if (effect.is(EditorView.announce)) {
8299
- if (first)
8336
+ if (first) {
8300
8337
  this.announceDOM.textContent = "";
8301
- first = false;
8338
+ this.win.clearTimeout(this.clearAnnouncement);
8339
+ this.clearAnnouncement = this.win.setTimeout(() => {
8340
+ this.announceDOM.textContent = "\u00a0";
8341
+ }, 200);
8342
+ first = false;
8343
+ }
8302
8344
  let div = this.announceDOM.appendChild(document.createElement("div"));
8303
8345
  div.textContent = effect.value;
8304
8346
  }
@@ -8447,15 +8489,11 @@ class EditorView {
8447
8489
  return skipAtoms(this, start, moveByChar(this, start, forward, initial => byGroup(this, start.head, initial)));
8448
8490
  }
8449
8491
  /**
8450
- Get the cursor position visually at the start or end of a line.
8451
- Note that this may differ from the _logical_ position at its
8452
- start or end (which is simply at `line.from`/`line.to`) if text
8453
- at the start or end goes against the line's base text direction.
8492
+ **\[DEPRECATED]** Get the cursor position visually at the start
8493
+ or end of a line.
8454
8494
  */
8455
8495
  visualLineSide(line, end) {
8456
- let order = this.bidiSpans(line), dir = this.textDirectionAt(line.from);
8457
- let span = order[end ? order.length - 1 : 0];
8458
- return EditorSelection.cursor(span.side(end, dir) + line.from, span.forward(!end, dir) ? 1 : -1);
8496
+ return end ? EditorSelection.cursor(line.to, 1) : EditorSelection.cursor(line.from, -1);
8459
8497
  }
8460
8498
  /**
8461
8499
  Move to the next line boundary in the given direction. If
@@ -8524,6 +8562,20 @@ class EditorView {
8524
8562
  this.readMeasured();
8525
8563
  let line = this.state.doc.lineAt(pos), order = this.bidiSpans(line);
8526
8564
  let span = order[BidiSpan.find(order, pos - line.from, -1, side)];
8565
+ // For positions at start/end of line, if the side points out of
8566
+ // the line and the text there isn't in the dominant dir, return
8567
+ // the position of the outermost character on the line.
8568
+ if (line.length && (pos == line.from && side < 0 || pos == line.to && side > 0) &&
8569
+ span.dir != this.textDirectionAt(line.from)) {
8570
+ if (pos == line.to) {
8571
+ pos = line.from + span.from;
8572
+ side = 1;
8573
+ }
8574
+ else {
8575
+ pos = line.from + span.to;
8576
+ side = -1;
8577
+ }
8578
+ }
8527
8579
  return this.docView.coordsAt(pos, side, span.dir == Direction.RTL);
8528
8580
  }
8529
8581
  /**
@@ -8648,6 +8700,7 @@ class EditorView {
8648
8700
  this.docView.destroy();
8649
8701
  this.dom.remove();
8650
8702
  this.observer.destroy();
8703
+ this.win.clearTimeout(this.clearAnnouncement);
8651
8704
  if (this.measureScheduled > -1)
8652
8705
  this.win.cancelAnimationFrame(this.measureScheduled);
8653
8706
  this.destroyed = true;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.43.11",
3
+ "version": "6.43.13",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",