@codemirror/view 0.20.4 → 0.20.7

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
+ ## 0.20.7 (2022-05-30)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue on Chrome Android where the DOM could fail to display the actual document after backspace.
6
+
7
+ Avoid an issue on Chrome Android where DOM changes were sometimes inappropriately replace by a backspace key effect due to spurious beforeinput events.
8
+
9
+ Fix a problem where the content element's width didn't cover the width of the actual content.
10
+
11
+ Work around a bug in Chrome 102 which caused wheel scrolling of the editor to be interrupted every few lines.
12
+
13
+ ## 0.20.6 (2022-05-20)
14
+
15
+ ### Bug fixes
16
+
17
+ Make sure the editor re-measures itself when its attributes are updated.
18
+
19
+ ## 0.20.5 (2022-05-18)
20
+
21
+ ### Bug fixes
22
+
23
+ Fix an issue where gutter elements without any markers in them would not get the `cm-gutterElement` class assigned.
24
+
25
+ Fix an issue where DOM event handlers registered by plugins were retained indefinitely, even after the editor was reconfigured.
26
+
1
27
  ## 0.20.4 (2022-05-03)
2
28
 
3
29
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -754,7 +754,7 @@ function textCoords(text, pos, side) {
754
754
  from--;
755
755
  flatten = 1;
756
756
  } // FIXME this is wrong in RTL text
757
- else {
757
+ else if (to < length) {
758
758
  to++;
759
759
  flatten = -1;
760
760
  }
@@ -763,7 +763,7 @@ function textCoords(text, pos, side) {
763
763
  else {
764
764
  if (side < 0)
765
765
  from--;
766
- else
766
+ else if (to < length)
767
767
  to++;
768
768
  }
769
769
  let rects = textRange(text, from, to).getClientRects();
@@ -1063,14 +1063,16 @@ function attrsEq(a, b) {
1063
1063
  return true;
1064
1064
  }
1065
1065
  function updateAttrs(dom, prev, attrs) {
1066
+ let changed = null;
1066
1067
  if (prev)
1067
1068
  for (let name in prev)
1068
1069
  if (!(attrs && name in attrs))
1069
- dom.removeAttribute(name);
1070
+ dom.removeAttribute(changed = name);
1070
1071
  if (attrs)
1071
1072
  for (let name in attrs)
1072
1073
  if (!(prev && prev[name] == attrs[name]))
1073
- dom.setAttribute(name, attrs[name]);
1074
+ dom.setAttribute(changed = name, attrs[name]);
1075
+ return !!changed;
1074
1076
  }
1075
1077
 
1076
1078
  /**
@@ -1377,6 +1379,7 @@ class LineView extends ContentView {
1377
1379
  transferDOM(other) {
1378
1380
  if (!this.dom)
1379
1381
  return;
1382
+ this.markDirty();
1380
1383
  other.setDOM(this.dom);
1381
1384
  other.prevAttrs = this.prevAttrs === undefined ? this.attrs : this.prevAttrs;
1382
1385
  this.prevAttrs = undefined;
@@ -2508,7 +2511,7 @@ class DocView extends ContentView {
2508
2511
  // no relayout is triggered and I cannot imagine how it can
2509
2512
  // recompute the scroll position without a layout)
2510
2513
  this.dom.style.height = this.view.viewState.contentHeight + "px";
2511
- this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
2514
+ this.dom.style.flexBasis = this.minWidth ? this.minWidth + "px" : "";
2512
2515
  // Chrome will sometimes, when DOM mutations occur directly
2513
2516
  // around the selection, get confused and report a different
2514
2517
  // selection from the one it displays (issue #218). This tries
@@ -3266,6 +3269,7 @@ class InputState {
3266
3269
  constructor(view) {
3267
3270
  this.lastKeyCode = 0;
3268
3271
  this.lastKeyTime = 0;
3272
+ this.chromeScrollHack = -1;
3269
3273
  // On iOS, some keys need to have their default behavior happen
3270
3274
  // (after which we retroactively handle them and reset the DOM) to
3271
3275
  // avoid messing up the virtual keyboard state.
@@ -3306,6 +3310,21 @@ class InputState {
3306
3310
  });
3307
3311
  this.registeredEvents.push(type);
3308
3312
  }
3313
+ if (browser.chrome && browser.chrome_version >= 102) {
3314
+ // On Chrome 102, viewport updates somehow stop wheel-based
3315
+ // scrolling. Turning off pointer events during the scroll seems
3316
+ // to avoid the issue.
3317
+ view.scrollDOM.addEventListener("wheel", () => {
3318
+ if (this.chromeScrollHack < 0)
3319
+ view.contentDOM.style.pointerEvents = "none";
3320
+ else
3321
+ window.clearTimeout(this.chromeScrollHack);
3322
+ this.chromeScrollHack = setTimeout(() => {
3323
+ this.chromeScrollHack = -1;
3324
+ view.contentDOM.style.pointerEvents = "";
3325
+ }, 100);
3326
+ }, { passive: true });
3327
+ }
3309
3328
  this.notifiedFocused = view.hasFocus;
3310
3329
  // On Safari adding an input event handler somehow prevents an
3311
3330
  // issue where the composition vanishes when you press enter.
@@ -3319,6 +3338,7 @@ class InputState {
3319
3338
  ensureHandlers(view, plugins) {
3320
3339
  var _a;
3321
3340
  let handlers;
3341
+ this.customHandlers = [];
3322
3342
  for (let plugin of plugins)
3323
3343
  if (handlers = (_a = plugin.update(view).spec) === null || _a === void 0 ? void 0 : _a.domEventHandlers) {
3324
3344
  this.customHandlers.push({ plugin: plugin.value, handlers });
@@ -4746,24 +4766,22 @@ class ViewState {
4746
4766
  this.defaultTextDirection = style.direction == "rtl" ? exports.Direction.RTL : exports.Direction.LTR;
4747
4767
  let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace);
4748
4768
  let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
4769
+ this.contentDOMHeight = dom.clientHeight;
4770
+ this.mustMeasureContent = false;
4749
4771
  let result = 0, bias = 0;
4772
+ // Vertical padding
4773
+ let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
4774
+ if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
4775
+ this.paddingTop = paddingTop;
4776
+ this.paddingBottom = paddingBottom;
4777
+ result |= 8 /* Geometry */ | 2 /* Height */;
4778
+ }
4750
4779
  if (this.editorWidth != view.scrollDOM.clientWidth) {
4751
4780
  if (oracle.lineWrapping)
4752
4781
  measureContent = true;
4753
4782
  this.editorWidth = view.scrollDOM.clientWidth;
4754
4783
  result |= 8 /* Geometry */;
4755
4784
  }
4756
- if (measureContent) {
4757
- this.mustMeasureContent = false;
4758
- this.contentDOMHeight = dom.clientHeight;
4759
- // Vertical padding
4760
- let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
4761
- if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
4762
- result |= 8 /* Geometry */;
4763
- this.paddingTop = paddingTop;
4764
- this.paddingBottom = paddingBottom;
4765
- }
4766
- }
4767
4785
  // Pixel viewport
4768
4786
  let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
4769
4787
  let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
@@ -5563,21 +5581,16 @@ class DOMObserver {
5563
5581
  // composition events that, when interrupted, cause text duplication
5564
5582
  // or other kinds of corruption. This hack makes the editor back off
5565
5583
  // from handling DOM changes for a moment when such a key is
5566
- // detected (via beforeinput or keydown), and then dispatches the
5567
- // key event, throwing away the DOM changes if it gets handled.
5584
+ // detected (via beforeinput or keydown), and then tries to flush
5585
+ // them or, if that has no effect, dispatches the given key.
5568
5586
  delayAndroidKey(key, keyCode) {
5569
5587
  if (!this.delayedAndroidKey)
5570
5588
  requestAnimationFrame(() => {
5571
5589
  let key = this.delayedAndroidKey;
5572
5590
  this.delayedAndroidKey = null;
5573
- let startState = this.view.state;
5574
- this.readSelectionRange();
5575
- if (dispatchKey(this.view.contentDOM, key.key, key.keyCode))
5576
- this.processRecords();
5577
- else
5578
- this.flush();
5579
- if (this.view.state == startState)
5580
- this.view.update([]);
5591
+ this.delayedFlush = -1;
5592
+ if (!this.flush())
5593
+ dispatchKey(this.view.contentDOM, key.key, key.keyCode);
5581
5594
  });
5582
5595
  // Since backspace beforeinput is sometimes signalled spuriously,
5583
5596
  // Enter always takes precedence.
@@ -5633,10 +5646,11 @@ class DOMObserver {
5633
5646
  return;
5634
5647
  this.selectionChanged = false;
5635
5648
  let startState = this.view.state;
5636
- this.onChange(from, to, typeOver);
5649
+ let handled = this.onChange(from, to, typeOver);
5637
5650
  // The view wasn't updated
5638
5651
  if (this.view.state == startState)
5639
5652
  this.view.update([]);
5653
+ return handled;
5640
5654
  }
5641
5655
  readMutation(rec) {
5642
5656
  let cView = this.view.docView.nearest(rec.target);
@@ -5719,7 +5733,7 @@ function applyDOMChange(view, start, end, typeOver) {
5719
5733
  if (start > -1) {
5720
5734
  let bounds = view.docView.domBoundsAround(start, end, 0);
5721
5735
  if (!bounds || view.state.readOnly)
5722
- return;
5736
+ return false;
5723
5737
  let { from, to } = bounds;
5724
5738
  let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
5725
5739
  let reader = new DOMReader(selPoints, view.state);
@@ -5759,7 +5773,7 @@ function applyDOMChange(view, start, end, typeOver) {
5759
5773
  newSel = state.EditorSelection.single(anchor, head);
5760
5774
  }
5761
5775
  if (!change && !newSel)
5762
- return;
5776
+ return false;
5763
5777
  // Heuristic to notice typing over a selected character
5764
5778
  if (!change && typeOver && !sel.empty && newSel && newSel.main.empty)
5765
5779
  change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) };
@@ -5775,13 +5789,13 @@ function applyDOMChange(view, start, end, typeOver) {
5775
5789
  };
5776
5790
  // Detect insert-period-on-double-space Mac behavior, and transform
5777
5791
  // it into a regular space insert.
5778
- else if (browser.mac && change && change.from == change.to && change.from == sel.head - 1 &&
5792
+ else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
5779
5793
  change.insert.toString() == ".")
5780
5794
  change = { from: sel.from, to: sel.to, insert: state.Text.of([" "]) };
5781
5795
  if (change) {
5782
5796
  let startState = view.state;
5783
5797
  if (browser.ios && view.inputState.flushIOSKey(view))
5784
- return;
5798
+ return true;
5785
5799
  // Android browsers don't fire reasonable key events for enter,
5786
5800
  // backspace, or delete. So this detects changes that look like
5787
5801
  // they're caused by those keys, and reinterprets them as key
@@ -5796,10 +5810,10 @@ function applyDOMChange(view, start, end, typeOver) {
5796
5810
  dispatchKey(view.contentDOM, "Backspace", 8)) ||
5797
5811
  (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
5798
5812
  dispatchKey(view.contentDOM, "Delete", 46))))
5799
- return;
5813
+ return true;
5800
5814
  let text = change.insert.toString();
5801
5815
  if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
5802
- return;
5816
+ return true;
5803
5817
  if (view.inputState.composing >= 0)
5804
5818
  view.inputState.composing++;
5805
5819
  let tr;
@@ -5855,6 +5869,7 @@ function applyDOMChange(view, start, end, typeOver) {
5855
5869
  }
5856
5870
  }
5857
5871
  view.dispatch(tr, { scrollIntoView: true, userEvent });
5872
+ return true;
5858
5873
  }
5859
5874
  else if (newSel && !newSel.main.eq(sel)) {
5860
5875
  let scrollIntoView = false, userEvent = "select";
@@ -5864,6 +5879,10 @@ function applyDOMChange(view, start, end, typeOver) {
5864
5879
  userEvent = view.inputState.lastSelectionOrigin;
5865
5880
  }
5866
5881
  view.dispatch({ selection: newSel, scrollIntoView, userEvent });
5882
+ return true;
5883
+ }
5884
+ else {
5885
+ return false;
5867
5886
  }
5868
5887
  }
5869
5888
  function findDiff(a, b, preferredPos, preferredSide) {
@@ -5982,7 +6001,7 @@ class EditorView {
5982
6001
  for (let plugin of this.plugins)
5983
6002
  plugin.update(this);
5984
6003
  this.observer = new DOMObserver(this, (from, to, typeOver) => {
5985
- applyDOMChange(this, from, to, typeOver);
6004
+ return applyDOMChange(this, from, to, typeOver);
5986
6005
  }, event => {
5987
6006
  this.inputState.runScrollHandlers(this, event);
5988
6007
  if (this.observer.intersecting)
@@ -6052,7 +6071,7 @@ class EditorView {
6052
6071
  update(transactions) {
6053
6072
  if (this.updateState != 0 /* Idle */)
6054
6073
  throw new Error("Calls to EditorView.update are not allowed while an update is in progress");
6055
- let redrawn = false, update;
6074
+ let redrawn = false, attrsChanged = false, update;
6056
6075
  let state$1 = this.state;
6057
6076
  for (let tr of transactions) {
6058
6077
  if (tr.startState != state$1)
@@ -6063,6 +6082,7 @@ class EditorView {
6063
6082
  this.viewState.state = state$1;
6064
6083
  return;
6065
6084
  }
6085
+ this.observer.clear();
6066
6086
  // When the phrases change, redraw the editor
6067
6087
  if (state$1.facet(state.EditorState.phrases) != this.state.facet(state.EditorState.phrases))
6068
6088
  return this.setState(state$1);
@@ -6090,7 +6110,7 @@ class EditorView {
6090
6110
  redrawn = this.docView.update(update);
6091
6111
  if (this.state.facet(styleModule) != this.styleModules)
6092
6112
  this.mountStyles();
6093
- this.updateAttrs();
6113
+ attrsChanged = this.updateAttrs();
6094
6114
  this.showAnnouncements(transactions);
6095
6115
  this.docView.updateSelection(redrawn, transactions.some(tr => tr.isUserEvent("select.pointer")));
6096
6116
  }
@@ -6099,7 +6119,7 @@ class EditorView {
6099
6119
  }
6100
6120
  if (update.startState.facet(theme) != update.state.facet(theme))
6101
6121
  this.viewState.mustMeasureContent = true;
6102
- if (redrawn || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
6122
+ if (redrawn || attrsChanged || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
6103
6123
  this.requestMeasure();
6104
6124
  if (!update.empty)
6105
6125
  for (let listener of this.state.facet(updateListener))
@@ -6279,12 +6299,14 @@ class EditorView {
6279
6299
  if (this.state.readOnly)
6280
6300
  contentAttrs["aria-readonly"] = "true";
6281
6301
  attrsFromFacet(this, contentAttributes, contentAttrs);
6282
- this.observer.ignore(() => {
6283
- updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
6284
- updateAttrs(this.dom, this.editorAttrs, editorAttrs);
6302
+ let changed = this.observer.ignore(() => {
6303
+ let changedContent = updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
6304
+ let changedEditor = updateAttrs(this.dom, this.editorAttrs, editorAttrs);
6305
+ return changedContent || changedEditor;
6285
6306
  });
6286
6307
  this.editorAttrs = editorAttrs;
6287
6308
  this.contentAttrs = contentAttrs;
6309
+ return changed;
6288
6310
  }
6289
6311
  showAnnouncements(trs) {
6290
6312
  let first = true;
@@ -8181,7 +8203,6 @@ class HoverPlugin {
8181
8203
  this.startHover();
8182
8204
  }
8183
8205
  startHover() {
8184
- var _a;
8185
8206
  clearTimeout(this.restartTimeout);
8186
8207
  let { lastMove } = this;
8187
8208
  let pos = this.view.contentDOM.contains(lastMove.target) ? this.view.posAtCoords(lastMove) : null;
@@ -8195,7 +8216,7 @@ class HoverPlugin {
8195
8216
  let bidi = this.view.bidiSpans(this.view.state.doc.lineAt(pos)).find(s => s.from <= pos && s.to >= pos);
8196
8217
  let rtl = bidi && bidi.dir == exports.Direction.RTL ? -1 : 1;
8197
8218
  let open = this.source(this.view, pos, (lastMove.x < posCoords.left ? -rtl : rtl));
8198
- if ((_a = open) === null || _a === void 0 ? void 0 : _a.then) {
8219
+ if (open === null || open === void 0 ? void 0 : open.then) {
8199
8220
  let pending = this.pending = { pos };
8200
8221
  open.then(result => {
8201
8222
  if (this.pending == pending) {
@@ -8795,6 +8816,7 @@ class GutterElement {
8795
8816
  this.above = 0;
8796
8817
  this.markers = [];
8797
8818
  this.dom = document.createElement("div");
8819
+ this.dom.className = "cm-gutterElement";
8798
8820
  this.update(view, height, above, markers);
8799
8821
  }
8800
8822
  update(view, height, above, markers) {
package/dist/index.js CHANGED
@@ -750,7 +750,7 @@ function textCoords(text, pos, side) {
750
750
  from--;
751
751
  flatten = 1;
752
752
  } // FIXME this is wrong in RTL text
753
- else {
753
+ else if (to < length) {
754
754
  to++;
755
755
  flatten = -1;
756
756
  }
@@ -759,7 +759,7 @@ function textCoords(text, pos, side) {
759
759
  else {
760
760
  if (side < 0)
761
761
  from--;
762
- else
762
+ else if (to < length)
763
763
  to++;
764
764
  }
765
765
  let rects = textRange(text, from, to).getClientRects();
@@ -1059,14 +1059,16 @@ function attrsEq(a, b) {
1059
1059
  return true;
1060
1060
  }
1061
1061
  function updateAttrs(dom, prev, attrs) {
1062
+ let changed = null;
1062
1063
  if (prev)
1063
1064
  for (let name in prev)
1064
1065
  if (!(attrs && name in attrs))
1065
- dom.removeAttribute(name);
1066
+ dom.removeAttribute(changed = name);
1066
1067
  if (attrs)
1067
1068
  for (let name in attrs)
1068
1069
  if (!(prev && prev[name] == attrs[name]))
1069
- dom.setAttribute(name, attrs[name]);
1070
+ dom.setAttribute(changed = name, attrs[name]);
1071
+ return !!changed;
1070
1072
  }
1071
1073
 
1072
1074
  /**
@@ -1372,6 +1374,7 @@ class LineView extends ContentView {
1372
1374
  transferDOM(other) {
1373
1375
  if (!this.dom)
1374
1376
  return;
1377
+ this.markDirty();
1375
1378
  other.setDOM(this.dom);
1376
1379
  other.prevAttrs = this.prevAttrs === undefined ? this.attrs : this.prevAttrs;
1377
1380
  this.prevAttrs = undefined;
@@ -2502,7 +2505,7 @@ class DocView extends ContentView {
2502
2505
  // no relayout is triggered and I cannot imagine how it can
2503
2506
  // recompute the scroll position without a layout)
2504
2507
  this.dom.style.height = this.view.viewState.contentHeight + "px";
2505
- this.dom.style.minWidth = this.minWidth ? this.minWidth + "px" : "";
2508
+ this.dom.style.flexBasis = this.minWidth ? this.minWidth + "px" : "";
2506
2509
  // Chrome will sometimes, when DOM mutations occur directly
2507
2510
  // around the selection, get confused and report a different
2508
2511
  // selection from the one it displays (issue #218). This tries
@@ -3260,6 +3263,7 @@ class InputState {
3260
3263
  constructor(view) {
3261
3264
  this.lastKeyCode = 0;
3262
3265
  this.lastKeyTime = 0;
3266
+ this.chromeScrollHack = -1;
3263
3267
  // On iOS, some keys need to have their default behavior happen
3264
3268
  // (after which we retroactively handle them and reset the DOM) to
3265
3269
  // avoid messing up the virtual keyboard state.
@@ -3300,6 +3304,21 @@ class InputState {
3300
3304
  });
3301
3305
  this.registeredEvents.push(type);
3302
3306
  }
3307
+ if (browser.chrome && browser.chrome_version >= 102) {
3308
+ // On Chrome 102, viewport updates somehow stop wheel-based
3309
+ // scrolling. Turning off pointer events during the scroll seems
3310
+ // to avoid the issue.
3311
+ view.scrollDOM.addEventListener("wheel", () => {
3312
+ if (this.chromeScrollHack < 0)
3313
+ view.contentDOM.style.pointerEvents = "none";
3314
+ else
3315
+ window.clearTimeout(this.chromeScrollHack);
3316
+ this.chromeScrollHack = setTimeout(() => {
3317
+ this.chromeScrollHack = -1;
3318
+ view.contentDOM.style.pointerEvents = "";
3319
+ }, 100);
3320
+ }, { passive: true });
3321
+ }
3303
3322
  this.notifiedFocused = view.hasFocus;
3304
3323
  // On Safari adding an input event handler somehow prevents an
3305
3324
  // issue where the composition vanishes when you press enter.
@@ -3313,6 +3332,7 @@ class InputState {
3313
3332
  ensureHandlers(view, plugins) {
3314
3333
  var _a;
3315
3334
  let handlers;
3335
+ this.customHandlers = [];
3316
3336
  for (let plugin of plugins)
3317
3337
  if (handlers = (_a = plugin.update(view).spec) === null || _a === void 0 ? void 0 : _a.domEventHandlers) {
3318
3338
  this.customHandlers.push({ plugin: plugin.value, handlers });
@@ -4739,24 +4759,22 @@ class ViewState {
4739
4759
  this.defaultTextDirection = style.direction == "rtl" ? Direction.RTL : Direction.LTR;
4740
4760
  let refresh = this.heightOracle.mustRefreshForWrapping(whiteSpace);
4741
4761
  let measureContent = refresh || this.mustMeasureContent || this.contentDOMHeight != dom.clientHeight;
4762
+ this.contentDOMHeight = dom.clientHeight;
4763
+ this.mustMeasureContent = false;
4742
4764
  let result = 0, bias = 0;
4765
+ // Vertical padding
4766
+ let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
4767
+ if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
4768
+ this.paddingTop = paddingTop;
4769
+ this.paddingBottom = paddingBottom;
4770
+ result |= 8 /* Geometry */ | 2 /* Height */;
4771
+ }
4743
4772
  if (this.editorWidth != view.scrollDOM.clientWidth) {
4744
4773
  if (oracle.lineWrapping)
4745
4774
  measureContent = true;
4746
4775
  this.editorWidth = view.scrollDOM.clientWidth;
4747
4776
  result |= 8 /* Geometry */;
4748
4777
  }
4749
- if (measureContent) {
4750
- this.mustMeasureContent = false;
4751
- this.contentDOMHeight = dom.clientHeight;
4752
- // Vertical padding
4753
- let paddingTop = parseInt(style.paddingTop) || 0, paddingBottom = parseInt(style.paddingBottom) || 0;
4754
- if (this.paddingTop != paddingTop || this.paddingBottom != paddingBottom) {
4755
- result |= 8 /* Geometry */;
4756
- this.paddingTop = paddingTop;
4757
- this.paddingBottom = paddingBottom;
4758
- }
4759
- }
4760
4778
  // Pixel viewport
4761
4779
  let pixelViewport = (this.printing ? fullPixelRange : visiblePixelRange)(dom, this.paddingTop);
4762
4780
  let dTop = pixelViewport.top - this.pixelViewport.top, dBottom = pixelViewport.bottom - this.pixelViewport.bottom;
@@ -5556,21 +5574,16 @@ class DOMObserver {
5556
5574
  // composition events that, when interrupted, cause text duplication
5557
5575
  // or other kinds of corruption. This hack makes the editor back off
5558
5576
  // from handling DOM changes for a moment when such a key is
5559
- // detected (via beforeinput or keydown), and then dispatches the
5560
- // key event, throwing away the DOM changes if it gets handled.
5577
+ // detected (via beforeinput or keydown), and then tries to flush
5578
+ // them or, if that has no effect, dispatches the given key.
5561
5579
  delayAndroidKey(key, keyCode) {
5562
5580
  if (!this.delayedAndroidKey)
5563
5581
  requestAnimationFrame(() => {
5564
5582
  let key = this.delayedAndroidKey;
5565
5583
  this.delayedAndroidKey = null;
5566
- let startState = this.view.state;
5567
- this.readSelectionRange();
5568
- if (dispatchKey(this.view.contentDOM, key.key, key.keyCode))
5569
- this.processRecords();
5570
- else
5571
- this.flush();
5572
- if (this.view.state == startState)
5573
- this.view.update([]);
5584
+ this.delayedFlush = -1;
5585
+ if (!this.flush())
5586
+ dispatchKey(this.view.contentDOM, key.key, key.keyCode);
5574
5587
  });
5575
5588
  // Since backspace beforeinput is sometimes signalled spuriously,
5576
5589
  // Enter always takes precedence.
@@ -5626,10 +5639,11 @@ class DOMObserver {
5626
5639
  return;
5627
5640
  this.selectionChanged = false;
5628
5641
  let startState = this.view.state;
5629
- this.onChange(from, to, typeOver);
5642
+ let handled = this.onChange(from, to, typeOver);
5630
5643
  // The view wasn't updated
5631
5644
  if (this.view.state == startState)
5632
5645
  this.view.update([]);
5646
+ return handled;
5633
5647
  }
5634
5648
  readMutation(rec) {
5635
5649
  let cView = this.view.docView.nearest(rec.target);
@@ -5712,7 +5726,7 @@ function applyDOMChange(view, start, end, typeOver) {
5712
5726
  if (start > -1) {
5713
5727
  let bounds = view.docView.domBoundsAround(start, end, 0);
5714
5728
  if (!bounds || view.state.readOnly)
5715
- return;
5729
+ return false;
5716
5730
  let { from, to } = bounds;
5717
5731
  let selPoints = view.docView.impreciseHead || view.docView.impreciseAnchor ? [] : selectionPoints(view);
5718
5732
  let reader = new DOMReader(selPoints, view.state);
@@ -5752,7 +5766,7 @@ function applyDOMChange(view, start, end, typeOver) {
5752
5766
  newSel = EditorSelection.single(anchor, head);
5753
5767
  }
5754
5768
  if (!change && !newSel)
5755
- return;
5769
+ return false;
5756
5770
  // Heuristic to notice typing over a selected character
5757
5771
  if (!change && typeOver && !sel.empty && newSel && newSel.main.empty)
5758
5772
  change = { from: sel.from, to: sel.to, insert: view.state.doc.slice(sel.from, sel.to) };
@@ -5768,13 +5782,13 @@ function applyDOMChange(view, start, end, typeOver) {
5768
5782
  };
5769
5783
  // Detect insert-period-on-double-space Mac behavior, and transform
5770
5784
  // it into a regular space insert.
5771
- else if (browser.mac && change && change.from == change.to && change.from == sel.head - 1 &&
5785
+ else if ((browser.mac || browser.android) && change && change.from == change.to && change.from == sel.head - 1 &&
5772
5786
  change.insert.toString() == ".")
5773
5787
  change = { from: sel.from, to: sel.to, insert: Text.of([" "]) };
5774
5788
  if (change) {
5775
5789
  let startState = view.state;
5776
5790
  if (browser.ios && view.inputState.flushIOSKey(view))
5777
- return;
5791
+ return true;
5778
5792
  // Android browsers don't fire reasonable key events for enter,
5779
5793
  // backspace, or delete. So this detects changes that look like
5780
5794
  // they're caused by those keys, and reinterprets them as key
@@ -5789,10 +5803,10 @@ function applyDOMChange(view, start, end, typeOver) {
5789
5803
  dispatchKey(view.contentDOM, "Backspace", 8)) ||
5790
5804
  (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
5791
5805
  dispatchKey(view.contentDOM, "Delete", 46))))
5792
- return;
5806
+ return true;
5793
5807
  let text = change.insert.toString();
5794
5808
  if (view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text)))
5795
- return;
5809
+ return true;
5796
5810
  if (view.inputState.composing >= 0)
5797
5811
  view.inputState.composing++;
5798
5812
  let tr;
@@ -5848,6 +5862,7 @@ function applyDOMChange(view, start, end, typeOver) {
5848
5862
  }
5849
5863
  }
5850
5864
  view.dispatch(tr, { scrollIntoView: true, userEvent });
5865
+ return true;
5851
5866
  }
5852
5867
  else if (newSel && !newSel.main.eq(sel)) {
5853
5868
  let scrollIntoView = false, userEvent = "select";
@@ -5857,6 +5872,10 @@ function applyDOMChange(view, start, end, typeOver) {
5857
5872
  userEvent = view.inputState.lastSelectionOrigin;
5858
5873
  }
5859
5874
  view.dispatch({ selection: newSel, scrollIntoView, userEvent });
5875
+ return true;
5876
+ }
5877
+ else {
5878
+ return false;
5860
5879
  }
5861
5880
  }
5862
5881
  function findDiff(a, b, preferredPos, preferredSide) {
@@ -5975,7 +5994,7 @@ class EditorView {
5975
5994
  for (let plugin of this.plugins)
5976
5995
  plugin.update(this);
5977
5996
  this.observer = new DOMObserver(this, (from, to, typeOver) => {
5978
- applyDOMChange(this, from, to, typeOver);
5997
+ return applyDOMChange(this, from, to, typeOver);
5979
5998
  }, event => {
5980
5999
  this.inputState.runScrollHandlers(this, event);
5981
6000
  if (this.observer.intersecting)
@@ -6045,7 +6064,7 @@ class EditorView {
6045
6064
  update(transactions) {
6046
6065
  if (this.updateState != 0 /* Idle */)
6047
6066
  throw new Error("Calls to EditorView.update are not allowed while an update is in progress");
6048
- let redrawn = false, update;
6067
+ let redrawn = false, attrsChanged = false, update;
6049
6068
  let state = this.state;
6050
6069
  for (let tr of transactions) {
6051
6070
  if (tr.startState != state)
@@ -6056,6 +6075,7 @@ class EditorView {
6056
6075
  this.viewState.state = state;
6057
6076
  return;
6058
6077
  }
6078
+ this.observer.clear();
6059
6079
  // When the phrases change, redraw the editor
6060
6080
  if (state.facet(EditorState.phrases) != this.state.facet(EditorState.phrases))
6061
6081
  return this.setState(state);
@@ -6083,7 +6103,7 @@ class EditorView {
6083
6103
  redrawn = this.docView.update(update);
6084
6104
  if (this.state.facet(styleModule) != this.styleModules)
6085
6105
  this.mountStyles();
6086
- this.updateAttrs();
6106
+ attrsChanged = this.updateAttrs();
6087
6107
  this.showAnnouncements(transactions);
6088
6108
  this.docView.updateSelection(redrawn, transactions.some(tr => tr.isUserEvent("select.pointer")));
6089
6109
  }
@@ -6092,7 +6112,7 @@ class EditorView {
6092
6112
  }
6093
6113
  if (update.startState.facet(theme) != update.state.facet(theme))
6094
6114
  this.viewState.mustMeasureContent = true;
6095
- if (redrawn || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
6115
+ if (redrawn || attrsChanged || scrollTarget || this.viewState.mustEnforceCursorAssoc || this.viewState.mustMeasureContent)
6096
6116
  this.requestMeasure();
6097
6117
  if (!update.empty)
6098
6118
  for (let listener of this.state.facet(updateListener))
@@ -6272,12 +6292,14 @@ class EditorView {
6272
6292
  if (this.state.readOnly)
6273
6293
  contentAttrs["aria-readonly"] = "true";
6274
6294
  attrsFromFacet(this, contentAttributes, contentAttrs);
6275
- this.observer.ignore(() => {
6276
- updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
6277
- updateAttrs(this.dom, this.editorAttrs, editorAttrs);
6295
+ let changed = this.observer.ignore(() => {
6296
+ let changedContent = updateAttrs(this.contentDOM, this.contentAttrs, contentAttrs);
6297
+ let changedEditor = updateAttrs(this.dom, this.editorAttrs, editorAttrs);
6298
+ return changedContent || changedEditor;
6278
6299
  });
6279
6300
  this.editorAttrs = editorAttrs;
6280
6301
  this.contentAttrs = contentAttrs;
6302
+ return changed;
6281
6303
  }
6282
6304
  showAnnouncements(trs) {
6283
6305
  let first = true;
@@ -8174,7 +8196,6 @@ class HoverPlugin {
8174
8196
  this.startHover();
8175
8197
  }
8176
8198
  startHover() {
8177
- var _a;
8178
8199
  clearTimeout(this.restartTimeout);
8179
8200
  let { lastMove } = this;
8180
8201
  let pos = this.view.contentDOM.contains(lastMove.target) ? this.view.posAtCoords(lastMove) : null;
@@ -8188,7 +8209,7 @@ class HoverPlugin {
8188
8209
  let bidi = this.view.bidiSpans(this.view.state.doc.lineAt(pos)).find(s => s.from <= pos && s.to >= pos);
8189
8210
  let rtl = bidi && bidi.dir == Direction.RTL ? -1 : 1;
8190
8211
  let open = this.source(this.view, pos, (lastMove.x < posCoords.left ? -rtl : rtl));
8191
- if ((_a = open) === null || _a === void 0 ? void 0 : _a.then) {
8212
+ if (open === null || open === void 0 ? void 0 : open.then) {
8192
8213
  let pending = this.pending = { pos };
8193
8214
  open.then(result => {
8194
8215
  if (this.pending == pending) {
@@ -8788,6 +8809,7 @@ class GutterElement {
8788
8809
  this.above = 0;
8789
8810
  this.markers = [];
8790
8811
  this.dom = document.createElement("div");
8812
+ this.dom.className = "cm-gutterElement";
8791
8813
  this.update(view, height, above, markers);
8792
8814
  }
8793
8815
  update(view, height, above, markers) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "0.20.4",
3
+ "version": "0.20.7",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",