@codemirror/view 6.1.0 → 6.1.3

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,29 @@
1
+ ## 6.1.3 (2022-08-03)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a bug where a document that contains only non-printing characters would lead to bogus text measurements (and, from those, to crashing).
6
+
7
+ Make sure differences between estimated and actual block heights don't cause visible scroll glitches.
8
+
9
+ ## 6.1.2 (2022-07-27)
10
+
11
+ ### Bug fixes
12
+
13
+ Fix an issue where double tapping enter to confirm IME input and insert a newline on iOS would sometimes insert two newlines.
14
+
15
+ Fix an issue on iOS where a composition could get aborted if the editor scrolled on backspace.
16
+
17
+ ## 6.1.1 (2022-07-25)
18
+
19
+ ### Bug fixes
20
+
21
+ Make `highlightSpecialChars` replace directional isolate characters by default.
22
+
23
+ The editor will now try to suppress browsers' native behavior of resetting the selection in the editable content when the editable element is focused (programmatically, with tab, etc).
24
+
25
+ Fix a CSS issue that made it possible, when the gutters were wide enough, for them to overlap with the content.
26
+
1
27
  ## 6.1.0 (2022-07-19)
2
28
 
3
29
  ### New features
package/dist/index.cjs CHANGED
@@ -268,6 +268,31 @@ function clearAttributes(node) {
268
268
  while (node.attributes.length)
269
269
  node.removeAttributeNode(node.attributes[0]);
270
270
  }
271
+ function atElementStart(doc, selection) {
272
+ let node = selection.focusNode, offset = selection.focusOffset;
273
+ if (!node || selection.anchorNode != node || selection.anchorOffset != offset)
274
+ return false;
275
+ for (;;) {
276
+ if (offset) {
277
+ if (node.nodeType != 1)
278
+ return false;
279
+ let prev = node.childNodes[offset - 1];
280
+ if (prev.contentEditable == "false")
281
+ offset--;
282
+ else {
283
+ node = prev;
284
+ offset = maxOffset(node);
285
+ }
286
+ }
287
+ else if (node == doc) {
288
+ return true;
289
+ }
290
+ else {
291
+ offset = domIndex(node);
292
+ node = node.parentNode;
293
+ }
294
+ }
295
+ }
271
296
 
272
297
  class DOMPos {
273
298
  constructor(node, offset, precise = true) {
@@ -1448,15 +1473,17 @@ class LineView extends ContentView {
1448
1473
  return null;
1449
1474
  let totalWidth = 0;
1450
1475
  for (let child of this.children) {
1451
- if (!(child instanceof TextView))
1476
+ if (!(child instanceof TextView) || /[^ -~]/.test(child.text))
1452
1477
  return null;
1453
1478
  let rects = clientRectsFor(child.dom);
1454
1479
  if (rects.length != 1)
1455
1480
  return null;
1456
1481
  totalWidth += rects[0].width;
1457
1482
  }
1458
- return { lineHeight: this.dom.getBoundingClientRect().height,
1459
- charWidth: totalWidth / this.length };
1483
+ return !totalWidth ? null : {
1484
+ lineHeight: this.dom.getBoundingClientRect().height,
1485
+ charWidth: totalWidth / this.length
1486
+ };
1460
1487
  }
1461
1488
  coordsAt(pos, side) {
1462
1489
  return coordsInChildren(this, pos, side);
@@ -3289,6 +3316,10 @@ class InputState {
3289
3316
  constructor(view) {
3290
3317
  this.lastKeyCode = 0;
3291
3318
  this.lastKeyTime = 0;
3319
+ this.lastTouchTime = 0;
3320
+ this.lastFocusTime = 0;
3321
+ this.lastScrollTop = 0;
3322
+ this.lastScrollLeft = 0;
3292
3323
  this.chromeScrollHack = -1;
3293
3324
  // On iOS, some keys need to have their default behavior happen
3294
3325
  // (after which we retroactively handle them and reset the DOM) to
@@ -3327,10 +3358,10 @@ class InputState {
3327
3358
  event.preventDefault();
3328
3359
  else
3329
3360
  handler(view, event);
3330
- });
3361
+ }, handlerOptions[type]);
3331
3362
  this.registeredEvents.push(type);
3332
3363
  }
3333
- if (browser.chrome && browser.chrome_version >= 102) {
3364
+ if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
3334
3365
  // On Chrome 102, viewport updates somehow stop wheel-based
3335
3366
  // scrolling. Turning off pointer events during the scroll seems
3336
3367
  // to avoid the issue.
@@ -3390,6 +3421,8 @@ class InputState {
3390
3421
  return false;
3391
3422
  }
3392
3423
  runScrollHandlers(view, event) {
3424
+ this.lastScrollTop = view.scrollDOM.scrollTop;
3425
+ this.lastScrollLeft = view.scrollDOM.scrollLeft;
3393
3426
  for (let set of this.customHandlers) {
3394
3427
  let handler = set.handlers.scroll;
3395
3428
  if (handler) {
@@ -3450,7 +3483,7 @@ class InputState {
3450
3483
  // compositionend and keydown events are sometimes emitted in the
3451
3484
  // wrong order. The key event should still be ignored, even when
3452
3485
  // it happens after the compositionend event.
3453
- if (browser.safari && Date.now() - this.compositionEndedAt < 100) {
3486
+ if (browser.safari && !browser.ios && Date.now() - this.compositionEndedAt < 100) {
3454
3487
  this.compositionEndedAt = 0;
3455
3488
  return true;
3456
3489
  }
@@ -3578,6 +3611,7 @@ function eventBelongsToEditor(view, event) {
3578
3611
  return true;
3579
3612
  }
3580
3613
  const handlers = Object.create(null);
3614
+ const handlerOptions = Object.create(null);
3581
3615
  // This is very crude, but unfortunately both these browsers _pretend_
3582
3616
  // that they have a clipboard API—all the objects and methods are
3583
3617
  // there, they just don't work, and they are hard to test.
@@ -3634,17 +3668,17 @@ handlers.keydown = (view, event) => {
3634
3668
  else if (modifierCodes.indexOf(event.keyCode) < 0)
3635
3669
  view.inputState.lastEscPress = 0;
3636
3670
  };
3637
- let lastTouch = 0;
3638
3671
  handlers.touchstart = (view, e) => {
3639
- lastTouch = Date.now();
3672
+ view.inputState.lastTouchTime = Date.now();
3640
3673
  view.inputState.setSelectionOrigin("select.pointer");
3641
3674
  };
3642
3675
  handlers.touchmove = view => {
3643
3676
  view.inputState.setSelectionOrigin("select.pointer");
3644
3677
  };
3678
+ handlerOptions.touchstart = handlerOptions.touchmove = { passive: true };
3645
3679
  handlers.mousedown = (view, event) => {
3646
3680
  view.observer.flush();
3647
- if (lastTouch > Date.now() - 2000 && getClickType(event) == 1)
3681
+ if (view.inputState.lastTouchTime > Date.now() - 2000 && getClickType(event) == 1)
3648
3682
  return; // Ignore touch interaction
3649
3683
  let style = null;
3650
3684
  for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
@@ -3898,7 +3932,15 @@ function updateForFocusChange(view) {
3898
3932
  view.update([]);
3899
3933
  }, 10);
3900
3934
  }
3901
- handlers.focus = updateForFocusChange;
3935
+ handlers.focus = view => {
3936
+ view.inputState.lastFocusTime = Date.now();
3937
+ // When focusing reset the scroll position, move it back to where it was
3938
+ if (!view.scrollDOM.scrollTop && (view.inputState.lastScrollTop || view.inputState.lastScrollLeft)) {
3939
+ view.scrollDOM.scrollTop = view.inputState.lastScrollTop;
3940
+ view.scrollDOM.scrollLeft = view.inputState.lastScrollLeft;
3941
+ }
3942
+ updateForFocusChange(view);
3943
+ };
3902
3944
  handlers.blur = view => {
3903
3945
  view.observer.clearSelectionRange();
3904
3946
  updateForFocusChange(view);
@@ -4635,7 +4677,7 @@ function visiblePixelRange(dom, paddingTop) {
4635
4677
  left = Math.max(left, parentRect.left);
4636
4678
  right = Math.min(right, parentRect.right);
4637
4679
  top = Math.max(top, parentRect.top);
4638
- bottom = Math.min(bottom, parentRect.bottom);
4680
+ bottom = parent == dom.parentNode ? parentRect.bottom : Math.min(bottom, parentRect.bottom);
4639
4681
  }
4640
4682
  parent = style.position == "absolute" || style.position == "fixed" ? elt.offsetParent : elt.parentNode;
4641
4683
  }
@@ -5269,6 +5311,7 @@ const baseTheme$1 = buildTheme("." + baseThemeID, {
5269
5311
  "&light .cm-specialChar": { color: "red" },
5270
5312
  "&dark .cm-specialChar": { color: "#f78" },
5271
5313
  ".cm-gutters": {
5314
+ flexShrink: 0,
5272
5315
  display: "flex",
5273
5316
  height: "100%",
5274
5317
  boxSizing: "border-box",
@@ -5525,15 +5568,28 @@ class DOMObserver {
5525
5568
  this.flush(false);
5526
5569
  }
5527
5570
  readSelectionRange() {
5528
- let { root } = this.view;
5571
+ let { view } = this;
5529
5572
  // The Selection object is broken in shadow roots in Safari. See
5530
5573
  // https://github.com/codemirror/dev/issues/414
5531
- let range = browser.safari && root.nodeType == 11 && deepActiveElement() == this.view.contentDOM &&
5532
- safariSelectionRangeHack(this.view) || getSelection(root);
5574
+ let range = browser.safari && view.root.nodeType == 11 && deepActiveElement() == this.dom &&
5575
+ safariSelectionRangeHack(this.view) || getSelection(view.root);
5533
5576
  if (!range || this.selectionRange.eq(range))
5534
5577
  return false;
5578
+ let local = hasSelection(this.dom, range);
5579
+ // Detect the situation where the browser has, on focus, moved the
5580
+ // selection to the start of the content element. Reset it to the
5581
+ // position from the editor state.
5582
+ if (local && !this.selectionChanged && this.selectionRange.focusNode &&
5583
+ view.inputState.lastFocusTime > Date.now() - 200 &&
5584
+ view.inputState.lastTouchTime < Date.now() - 300 &&
5585
+ atElementStart(this.dom, range)) {
5586
+ view.docView.updateSelection();
5587
+ return false;
5588
+ }
5535
5589
  this.selectionRange.setRange(range);
5536
- return this.selectionChanged = true;
5590
+ if (local)
5591
+ this.selectionChanged = true;
5592
+ return true;
5537
5593
  }
5538
5594
  setSelectionRange(anchor, head) {
5539
5595
  this.selectionRange.set(anchor.node, anchor.offset, head.node, head.offset);
@@ -5620,7 +5676,7 @@ class DOMObserver {
5620
5676
  this.delayedAndroidKey = null;
5621
5677
  this.delayedFlush = -1;
5622
5678
  if (!this.flush())
5623
- dispatchKey(this.view.contentDOM, key.key, key.keyCode);
5679
+ dispatchKey(this.dom, key.key, key.keyCode);
5624
5680
  });
5625
5681
  // Since backspace beforeinput is sometimes signalled spuriously,
5626
5682
  // Enter always takes precedence.
@@ -5635,8 +5691,8 @@ class DOMObserver {
5635
5691
  if (this.delayedFlush >= 0) {
5636
5692
  window.clearTimeout(this.delayedFlush);
5637
5693
  this.delayedFlush = -1;
5638
- this.flush();
5639
5694
  }
5695
+ this.flush();
5640
5696
  }
5641
5697
  processRecords() {
5642
5698
  let records = this.queue;
@@ -5674,6 +5730,7 @@ class DOMObserver {
5674
5730
  let newSel = this.selectionChanged && hasSelection(this.dom, this.selectionRange);
5675
5731
  if (from < 0 && !newSel)
5676
5732
  return;
5733
+ this.view.inputState.lastFocusTime = 0;
5677
5734
  this.selectionChanged = false;
5678
5735
  let startState = this.view.state;
5679
5736
  let handled = this.onChange(from, to, typeOver);
@@ -6227,12 +6284,15 @@ class EditorView {
6227
6284
  cancelAnimationFrame(this.measureScheduled);
6228
6285
  this.measureScheduled = 0; // Prevent requestMeasure calls from scheduling another animation frame
6229
6286
  if (flush)
6230
- this.observer.flush();
6287
+ this.observer.forceFlush();
6231
6288
  let updated = null;
6289
+ let { scrollHeight, scrollTop, clientHeight } = this.scrollDOM;
6290
+ let refHeight = scrollTop > scrollHeight - clientHeight - 4 ? scrollHeight : scrollTop;
6232
6291
  try {
6233
6292
  for (let i = 0;; i++) {
6234
6293
  this.updateState = 1 /* Measuring */;
6235
6294
  let oldViewport = this.viewport;
6295
+ let refBlock = this.viewState.lineBlockAtHeight(refHeight);
6236
6296
  let changed = this.viewState.measure(this);
6237
6297
  if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
6238
6298
  break;
@@ -6284,6 +6344,13 @@ class EditorView {
6284
6344
  this.viewState.scrollTarget = null;
6285
6345
  scrolled = true;
6286
6346
  }
6347
+ else {
6348
+ let diff = this.viewState.lineBlockAt(refBlock.from).top - refBlock.top;
6349
+ if (diff > 1 || diff < -1) {
6350
+ this.scrollDOM.scrollTop += diff;
6351
+ scrolled = true;
6352
+ }
6353
+ }
6287
6354
  if (redrawn)
6288
6355
  this.docView.updateSelection(true);
6289
6356
  if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
@@ -7495,7 +7562,7 @@ class MatchDecorator {
7495
7562
  }
7496
7563
 
7497
7564
  const UnicodeRegexpSupport = /x/.unicode != null ? "gu" : "g";
7498
- const Specials = new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport);
7565
+ const Specials = new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport);
7499
7566
  const Names = {
7500
7567
  0: "null",
7501
7568
  7: "bell",
@@ -7512,6 +7579,9 @@ const Names = {
7512
7579
  8232: "line separator",
7513
7580
  8237: "left-to-right override",
7514
7581
  8238: "right-to-left override",
7582
+ 8294: "left-to-right isolate",
7583
+ 8295: "right-to-left isolate",
7584
+ 8297: "pop directional isolate",
7515
7585
  8233: "paragraph separator",
7516
7586
  65279: "zero width no-break space",
7517
7587
  65532: "object replacement"
package/dist/index.js CHANGED
@@ -264,6 +264,31 @@ function clearAttributes(node) {
264
264
  while (node.attributes.length)
265
265
  node.removeAttributeNode(node.attributes[0]);
266
266
  }
267
+ function atElementStart(doc, selection) {
268
+ let node = selection.focusNode, offset = selection.focusOffset;
269
+ if (!node || selection.anchorNode != node || selection.anchorOffset != offset)
270
+ return false;
271
+ for (;;) {
272
+ if (offset) {
273
+ if (node.nodeType != 1)
274
+ return false;
275
+ let prev = node.childNodes[offset - 1];
276
+ if (prev.contentEditable == "false")
277
+ offset--;
278
+ else {
279
+ node = prev;
280
+ offset = maxOffset(node);
281
+ }
282
+ }
283
+ else if (node == doc) {
284
+ return true;
285
+ }
286
+ else {
287
+ offset = domIndex(node);
288
+ node = node.parentNode;
289
+ }
290
+ }
291
+ }
267
292
 
268
293
  class DOMPos {
269
294
  constructor(node, offset, precise = true) {
@@ -1443,15 +1468,17 @@ class LineView extends ContentView {
1443
1468
  return null;
1444
1469
  let totalWidth = 0;
1445
1470
  for (let child of this.children) {
1446
- if (!(child instanceof TextView))
1471
+ if (!(child instanceof TextView) || /[^ -~]/.test(child.text))
1447
1472
  return null;
1448
1473
  let rects = clientRectsFor(child.dom);
1449
1474
  if (rects.length != 1)
1450
1475
  return null;
1451
1476
  totalWidth += rects[0].width;
1452
1477
  }
1453
- return { lineHeight: this.dom.getBoundingClientRect().height,
1454
- charWidth: totalWidth / this.length };
1478
+ return !totalWidth ? null : {
1479
+ lineHeight: this.dom.getBoundingClientRect().height,
1480
+ charWidth: totalWidth / this.length
1481
+ };
1455
1482
  }
1456
1483
  coordsAt(pos, side) {
1457
1484
  return coordsInChildren(this, pos, side);
@@ -3283,6 +3310,10 @@ class InputState {
3283
3310
  constructor(view) {
3284
3311
  this.lastKeyCode = 0;
3285
3312
  this.lastKeyTime = 0;
3313
+ this.lastTouchTime = 0;
3314
+ this.lastFocusTime = 0;
3315
+ this.lastScrollTop = 0;
3316
+ this.lastScrollLeft = 0;
3286
3317
  this.chromeScrollHack = -1;
3287
3318
  // On iOS, some keys need to have their default behavior happen
3288
3319
  // (after which we retroactively handle them and reset the DOM) to
@@ -3321,10 +3352,10 @@ class InputState {
3321
3352
  event.preventDefault();
3322
3353
  else
3323
3354
  handler(view, event);
3324
- });
3355
+ }, handlerOptions[type]);
3325
3356
  this.registeredEvents.push(type);
3326
3357
  }
3327
- if (browser.chrome && browser.chrome_version >= 102) {
3358
+ if (browser.chrome && browser.chrome_version == 102) { // FIXME remove at some point
3328
3359
  // On Chrome 102, viewport updates somehow stop wheel-based
3329
3360
  // scrolling. Turning off pointer events during the scroll seems
3330
3361
  // to avoid the issue.
@@ -3384,6 +3415,8 @@ class InputState {
3384
3415
  return false;
3385
3416
  }
3386
3417
  runScrollHandlers(view, event) {
3418
+ this.lastScrollTop = view.scrollDOM.scrollTop;
3419
+ this.lastScrollLeft = view.scrollDOM.scrollLeft;
3387
3420
  for (let set of this.customHandlers) {
3388
3421
  let handler = set.handlers.scroll;
3389
3422
  if (handler) {
@@ -3444,7 +3477,7 @@ class InputState {
3444
3477
  // compositionend and keydown events are sometimes emitted in the
3445
3478
  // wrong order. The key event should still be ignored, even when
3446
3479
  // it happens after the compositionend event.
3447
- if (browser.safari && Date.now() - this.compositionEndedAt < 100) {
3480
+ if (browser.safari && !browser.ios && Date.now() - this.compositionEndedAt < 100) {
3448
3481
  this.compositionEndedAt = 0;
3449
3482
  return true;
3450
3483
  }
@@ -3572,6 +3605,7 @@ function eventBelongsToEditor(view, event) {
3572
3605
  return true;
3573
3606
  }
3574
3607
  const handlers = /*@__PURE__*/Object.create(null);
3608
+ const handlerOptions = /*@__PURE__*/Object.create(null);
3575
3609
  // This is very crude, but unfortunately both these browsers _pretend_
3576
3610
  // that they have a clipboard API—all the objects and methods are
3577
3611
  // there, they just don't work, and they are hard to test.
@@ -3628,17 +3662,17 @@ handlers.keydown = (view, event) => {
3628
3662
  else if (modifierCodes.indexOf(event.keyCode) < 0)
3629
3663
  view.inputState.lastEscPress = 0;
3630
3664
  };
3631
- let lastTouch = 0;
3632
3665
  handlers.touchstart = (view, e) => {
3633
- lastTouch = Date.now();
3666
+ view.inputState.lastTouchTime = Date.now();
3634
3667
  view.inputState.setSelectionOrigin("select.pointer");
3635
3668
  };
3636
3669
  handlers.touchmove = view => {
3637
3670
  view.inputState.setSelectionOrigin("select.pointer");
3638
3671
  };
3672
+ handlerOptions.touchstart = handlerOptions.touchmove = { passive: true };
3639
3673
  handlers.mousedown = (view, event) => {
3640
3674
  view.observer.flush();
3641
- if (lastTouch > Date.now() - 2000 && getClickType(event) == 1)
3675
+ if (view.inputState.lastTouchTime > Date.now() - 2000 && getClickType(event) == 1)
3642
3676
  return; // Ignore touch interaction
3643
3677
  let style = null;
3644
3678
  for (let makeStyle of view.state.facet(mouseSelectionStyle)) {
@@ -3892,7 +3926,15 @@ function updateForFocusChange(view) {
3892
3926
  view.update([]);
3893
3927
  }, 10);
3894
3928
  }
3895
- handlers.focus = updateForFocusChange;
3929
+ handlers.focus = view => {
3930
+ view.inputState.lastFocusTime = Date.now();
3931
+ // When focusing reset the scroll position, move it back to where it was
3932
+ if (!view.scrollDOM.scrollTop && (view.inputState.lastScrollTop || view.inputState.lastScrollLeft)) {
3933
+ view.scrollDOM.scrollTop = view.inputState.lastScrollTop;
3934
+ view.scrollDOM.scrollLeft = view.inputState.lastScrollLeft;
3935
+ }
3936
+ updateForFocusChange(view);
3937
+ };
3896
3938
  handlers.blur = view => {
3897
3939
  view.observer.clearSelectionRange();
3898
3940
  updateForFocusChange(view);
@@ -4628,7 +4670,7 @@ function visiblePixelRange(dom, paddingTop) {
4628
4670
  left = Math.max(left, parentRect.left);
4629
4671
  right = Math.min(right, parentRect.right);
4630
4672
  top = Math.max(top, parentRect.top);
4631
- bottom = Math.min(bottom, parentRect.bottom);
4673
+ bottom = parent == dom.parentNode ? parentRect.bottom : Math.min(bottom, parentRect.bottom);
4632
4674
  }
4633
4675
  parent = style.position == "absolute" || style.position == "fixed" ? elt.offsetParent : elt.parentNode;
4634
4676
  }
@@ -5262,6 +5304,7 @@ const baseTheme$1 = /*@__PURE__*/buildTheme("." + baseThemeID, {
5262
5304
  "&light .cm-specialChar": { color: "red" },
5263
5305
  "&dark .cm-specialChar": { color: "#f78" },
5264
5306
  ".cm-gutters": {
5307
+ flexShrink: 0,
5265
5308
  display: "flex",
5266
5309
  height: "100%",
5267
5310
  boxSizing: "border-box",
@@ -5518,15 +5561,28 @@ class DOMObserver {
5518
5561
  this.flush(false);
5519
5562
  }
5520
5563
  readSelectionRange() {
5521
- let { root } = this.view;
5564
+ let { view } = this;
5522
5565
  // The Selection object is broken in shadow roots in Safari. See
5523
5566
  // https://github.com/codemirror/dev/issues/414
5524
- let range = browser.safari && root.nodeType == 11 && deepActiveElement() == this.view.contentDOM &&
5525
- safariSelectionRangeHack(this.view) || getSelection(root);
5567
+ let range = browser.safari && view.root.nodeType == 11 && deepActiveElement() == this.dom &&
5568
+ safariSelectionRangeHack(this.view) || getSelection(view.root);
5526
5569
  if (!range || this.selectionRange.eq(range))
5527
5570
  return false;
5571
+ let local = hasSelection(this.dom, range);
5572
+ // Detect the situation where the browser has, on focus, moved the
5573
+ // selection to the start of the content element. Reset it to the
5574
+ // position from the editor state.
5575
+ if (local && !this.selectionChanged && this.selectionRange.focusNode &&
5576
+ view.inputState.lastFocusTime > Date.now() - 200 &&
5577
+ view.inputState.lastTouchTime < Date.now() - 300 &&
5578
+ atElementStart(this.dom, range)) {
5579
+ view.docView.updateSelection();
5580
+ return false;
5581
+ }
5528
5582
  this.selectionRange.setRange(range);
5529
- return this.selectionChanged = true;
5583
+ if (local)
5584
+ this.selectionChanged = true;
5585
+ return true;
5530
5586
  }
5531
5587
  setSelectionRange(anchor, head) {
5532
5588
  this.selectionRange.set(anchor.node, anchor.offset, head.node, head.offset);
@@ -5613,7 +5669,7 @@ class DOMObserver {
5613
5669
  this.delayedAndroidKey = null;
5614
5670
  this.delayedFlush = -1;
5615
5671
  if (!this.flush())
5616
- dispatchKey(this.view.contentDOM, key.key, key.keyCode);
5672
+ dispatchKey(this.dom, key.key, key.keyCode);
5617
5673
  });
5618
5674
  // Since backspace beforeinput is sometimes signalled spuriously,
5619
5675
  // Enter always takes precedence.
@@ -5628,8 +5684,8 @@ class DOMObserver {
5628
5684
  if (this.delayedFlush >= 0) {
5629
5685
  window.clearTimeout(this.delayedFlush);
5630
5686
  this.delayedFlush = -1;
5631
- this.flush();
5632
5687
  }
5688
+ this.flush();
5633
5689
  }
5634
5690
  processRecords() {
5635
5691
  let records = this.queue;
@@ -5667,6 +5723,7 @@ class DOMObserver {
5667
5723
  let newSel = this.selectionChanged && hasSelection(this.dom, this.selectionRange);
5668
5724
  if (from < 0 && !newSel)
5669
5725
  return;
5726
+ this.view.inputState.lastFocusTime = 0;
5670
5727
  this.selectionChanged = false;
5671
5728
  let startState = this.view.state;
5672
5729
  let handled = this.onChange(from, to, typeOver);
@@ -6220,12 +6277,15 @@ class EditorView {
6220
6277
  cancelAnimationFrame(this.measureScheduled);
6221
6278
  this.measureScheduled = 0; // Prevent requestMeasure calls from scheduling another animation frame
6222
6279
  if (flush)
6223
- this.observer.flush();
6280
+ this.observer.forceFlush();
6224
6281
  let updated = null;
6282
+ let { scrollHeight, scrollTop, clientHeight } = this.scrollDOM;
6283
+ let refHeight = scrollTop > scrollHeight - clientHeight - 4 ? scrollHeight : scrollTop;
6225
6284
  try {
6226
6285
  for (let i = 0;; i++) {
6227
6286
  this.updateState = 1 /* Measuring */;
6228
6287
  let oldViewport = this.viewport;
6288
+ let refBlock = this.viewState.lineBlockAtHeight(refHeight);
6229
6289
  let changed = this.viewState.measure(this);
6230
6290
  if (!changed && !this.measureRequests.length && this.viewState.scrollTarget == null)
6231
6291
  break;
@@ -6277,6 +6337,13 @@ class EditorView {
6277
6337
  this.viewState.scrollTarget = null;
6278
6338
  scrolled = true;
6279
6339
  }
6340
+ else {
6341
+ let diff = this.viewState.lineBlockAt(refBlock.from).top - refBlock.top;
6342
+ if (diff > 1 || diff < -1) {
6343
+ this.scrollDOM.scrollTop += diff;
6344
+ scrolled = true;
6345
+ }
6346
+ }
6280
6347
  if (redrawn)
6281
6348
  this.docView.updateSelection(true);
6282
6349
  if (this.viewport.from == oldViewport.from && this.viewport.to == oldViewport.to &&
@@ -7488,7 +7555,7 @@ class MatchDecorator {
7488
7555
  }
7489
7556
 
7490
7557
  const UnicodeRegexpSupport = /x/.unicode != null ? "gu" : "g";
7491
- const Specials = /*@__PURE__*/new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport);
7558
+ const Specials = /*@__PURE__*/new RegExp("[\u0000-\u0008\u000a-\u001f\u007f-\u009f\u00ad\u061c\u200b\u200e\u200f\u2028\u2029\u202d\u202e\u2066\u2067\u2069\ufeff\ufff9-\ufffc]", UnicodeRegexpSupport);
7492
7559
  const Names = {
7493
7560
  0: "null",
7494
7561
  7: "bell",
@@ -7505,6 +7572,9 @@ const Names = {
7505
7572
  8232: "line separator",
7506
7573
  8237: "left-to-right override",
7507
7574
  8238: "right-to-left override",
7575
+ 8294: "left-to-right isolate",
7576
+ 8295: "right-to-left isolate",
7577
+ 8297: "pop directional isolate",
7508
7578
  8233: "paragraph separator",
7509
7579
  65279: "zero width no-break space",
7510
7580
  65532: "object replacement"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.1.0",
3
+ "version": "6.1.3",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",