@codemirror/view 6.26.4-edit-context.1 → 6.26.4

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,21 @@
1
+ ## 6.26.4 (2024-06-04)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix an issue where commands with an optional second argument would get the keyboard event in that argument when called from a keymap.
6
+
7
+ Fix an issue that could cause the cursor to be rendered on the wrong side of a zero-length block widget.
8
+
9
+ Fix an issue where `drawSelection` got confused by block widgets in line-wrapped editors in some situations.
10
+
11
+ Don't hide the native selection in widgets that have focus.
12
+
13
+ Make sure that clicking an unfocusable editor still remove focus from any other focused elements.
14
+
15
+ Fix a crash when loading the package in a non-browser environment.
16
+
17
+ Stop mouse selection when the user types.
18
+
1
19
  ## 6.26.3 (2024-04-12)
2
20
 
3
21
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -745,7 +745,7 @@ var browser = {
745
745
  android: /Android\b/.test(nav.userAgent),
746
746
  webkit,
747
747
  safari,
748
- webkit_version: webkit ? +(/\bAppleWebKit\/(\d+)/.exec(navigator.userAgent) || [0, 0])[1] : 0,
748
+ webkit_version: webkit ? +(/\bAppleWebKit\/(\d+)/.exec(nav.userAgent) || [0, 0])[1] : 0,
749
749
  tabSize: doc.documentElement.style.tabSize != null ? "tab-size" : "-moz-tab-size"
750
750
  };
751
751
 
@@ -2368,7 +2368,6 @@ class ScrollTarget {
2368
2368
  }
2369
2369
  }
2370
2370
  const scrollIntoView = state.StateEffect.define({ map: (t, ch) => t.map(ch) });
2371
- const setEditContextFormatting = state.StateEffect.define();
2372
2371
  /**
2373
2372
  Log or report an unhandled exception in client code. Should
2374
2373
  probably only be used by extension code that allows client code to
@@ -2705,11 +2704,10 @@ class DocView extends ContentView {
2705
2704
  super();
2706
2705
  this.view = view;
2707
2706
  this.decorations = [];
2708
- this.dynamicDecorationMap = [false];
2707
+ this.dynamicDecorationMap = [];
2709
2708
  this.domChanged = null;
2710
2709
  this.hasComposition = null;
2711
2710
  this.markedForComposition = new Set;
2712
- this.editContextFormatting = Decoration.none;
2713
2711
  this.lastCompositionAfterCursor = false;
2714
2712
  // Track a minimum width for the editor. When measuring sizes in
2715
2713
  // measureVisibleLineHeights, this is updated to point at the width
@@ -2748,9 +2746,8 @@ class DocView extends ContentView {
2748
2746
  this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
2749
2747
  }
2750
2748
  }
2751
- this.updateEditContextFormatting(update);
2752
2749
  let readCompositionAt = -1;
2753
- if (this.view.inputState.composing >= 0 && !this.view.observer.editContext) {
2750
+ if (this.view.inputState.composing >= 0) {
2754
2751
  if ((_a = this.domChanged) === null || _a === void 0 ? void 0 : _a.newSel)
2755
2752
  readCompositionAt = this.domChanged.newSel.head;
2756
2753
  else if (!touchesComposition(update.changes, this.hasComposition) && !update.selectionSet)
@@ -2858,14 +2855,6 @@ class DocView extends ContentView {
2858
2855
  if (composition)
2859
2856
  this.fixCompositionDOM(composition);
2860
2857
  }
2861
- updateEditContextFormatting(update) {
2862
- this.editContextFormatting = this.editContextFormatting.map(update.changes);
2863
- for (let tr of update.transactions)
2864
- for (let effect of tr.effects)
2865
- if (effect.is(setEditContextFormatting)) {
2866
- this.editContextFormatting = effect.value;
2867
- }
2868
- }
2869
2858
  compositionView(composition) {
2870
2859
  let cur = new TextView(composition.text.nodeValue);
2871
2860
  cur.flags |= 8 /* ViewFlag.Composition */;
@@ -3071,6 +3060,12 @@ class DocView extends ContentView {
3071
3060
  best = child;
3072
3061
  bestPos = start;
3073
3062
  }
3063
+ else if (best && start == pos && end == pos && child instanceof BlockWidgetView && Math.abs(side) < 2) {
3064
+ if (child.deco.startSide < 0)
3065
+ break;
3066
+ else if (i)
3067
+ best = null;
3068
+ }
3074
3069
  off = start;
3075
3070
  }
3076
3071
  return best ? best.coordsAt(pos - bestPos, side) : null;
@@ -3191,7 +3186,7 @@ class DocView extends ContentView {
3191
3186
  return Decoration.set(deco);
3192
3187
  }
3193
3188
  updateDeco() {
3194
- let i = 1;
3189
+ let i = 0;
3195
3190
  let allDeco = this.view.state.facet(decorations).map(d => {
3196
3191
  let dynamic = this.dynamicDecorationMap[i++] = typeof d == "function";
3197
3192
  return dynamic ? d(this.view) : d;
@@ -3207,7 +3202,6 @@ class DocView extends ContentView {
3207
3202
  allDeco.push(state.RangeSet.join(outerDeco));
3208
3203
  }
3209
3204
  this.decorations = [
3210
- this.editContextFormatting,
3211
3205
  ...allDeco,
3212
3206
  this.computeBlockGapDeco(),
3213
3207
  this.view.viewState.lineGapDeco
@@ -3894,7 +3888,6 @@ class InputState {
3894
3888
  this.mouseSelection = mouseSelection;
3895
3889
  }
3896
3890
  update(update) {
3897
- this.view.observer.update(update);
3898
3891
  if (this.mouseSelection)
3899
3892
  this.mouseSelection.update(update);
3900
3893
  if (this.draggedContent && update.docChanged)
@@ -4073,7 +4066,9 @@ class MouseSelection {
4073
4066
  this.mustSelect = false;
4074
4067
  }
4075
4068
  update(update) {
4076
- if (this.style.update(update))
4069
+ if (update.transactions.some(tr => tr.isUserEvent("input.type")))
4070
+ this.destroy();
4071
+ else if (this.style.update(update))
4077
4072
  setTimeout(() => this.select(this.lastEvent), 20);
4078
4073
  }
4079
4074
  }
@@ -4197,7 +4192,12 @@ handlers.mousedown = (view, event) => {
4197
4192
  let mustFocus = !view.hasFocus;
4198
4193
  view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
4199
4194
  if (mustFocus)
4200
- view.observer.ignore(() => focusPreventScroll(view.contentDOM));
4195
+ view.observer.ignore(() => {
4196
+ focusPreventScroll(view.contentDOM);
4197
+ let active = view.root.activeElement;
4198
+ if (active && !active.contains(view.contentDOM))
4199
+ active.blur();
4200
+ });
4201
4201
  let mouseSel = view.inputState.mouseSelection;
4202
4202
  if (mouseSel) {
4203
4203
  mouseSel.start(event);
@@ -4485,8 +4485,6 @@ observers.blur = view => {
4485
4485
  updateForFocusChange(view);
4486
4486
  };
4487
4487
  observers.compositionstart = observers.compositionupdate = view => {
4488
- if (view.observer.editContext)
4489
- return; // Composition handled by edit context
4490
4488
  if (view.inputState.compositionFirstChange == null)
4491
4489
  view.inputState.compositionFirstChange = true;
4492
4490
  if (view.inputState.composing < 0) {
@@ -4495,8 +4493,6 @@ observers.compositionstart = observers.compositionupdate = view => {
4495
4493
  }
4496
4494
  };
4497
4495
  observers.compositionend = view => {
4498
- if (view.observer.editContext)
4499
- return; // Composition handled by edit context
4500
4496
  view.inputState.composing = -1;
4501
4497
  view.inputState.compositionEndedAt = Date.now();
4502
4498
  view.inputState.compositionPendingKey = true;
@@ -5416,9 +5412,12 @@ class ViewState {
5416
5412
  this.heightOracle = new HeightOracle(guessWrapping);
5417
5413
  this.stateDeco = state$1.facet(decorations).filter(d => typeof d != "function");
5418
5414
  this.heightMap = HeightMap.empty().applyChanges(this.stateDeco, state.Text.empty, this.heightOracle.setDoc(state$1.doc), [new ChangedRange(0, 0, 0, state$1.doc.length)]);
5419
- this.viewport = this.getViewport(0, null);
5415
+ for (let i = 0; i < 2; i++) {
5416
+ this.viewport = this.getViewport(0, null);
5417
+ if (!this.updateForViewport())
5418
+ break;
5419
+ }
5420
5420
  this.updateViewportLines();
5421
- this.updateForViewport();
5422
5421
  this.lineGaps = this.ensureLineGaps([]);
5423
5422
  this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(this, false)));
5424
5423
  this.computeVisibleRanges();
@@ -5433,13 +5432,18 @@ class ViewState {
5433
5432
  }
5434
5433
  }
5435
5434
  this.viewports = viewports.sort((a, b) => a.from - b.from);
5435
+ return this.updateScaler();
5436
+ }
5437
+ updateScaler() {
5438
+ let scaler = this.scaler;
5436
5439
  this.scaler = this.heightMap.height <= 7000000 /* VP.MaxDOMHeight */ ? IdScaler :
5437
5440
  new BigScaler(this.heightOracle, this.heightMap, this.viewports);
5441
+ return scaler.eq(this.scaler) ? 0 : 2 /* UpdateFlag.Height */;
5438
5442
  }
5439
5443
  updateViewportLines() {
5440
5444
  this.viewportLines = [];
5441
5445
  this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.heightOracle.setDoc(this.state.doc), 0, 0, block => {
5442
- this.viewportLines.push(this.scaler.scale == 1 ? block : scaleBlock(block, this.scaler));
5446
+ this.viewportLines.push(scaleBlock(block, this.scaler));
5443
5447
  });
5444
5448
  }
5445
5449
  update(update, scrollTarget = null) {
@@ -5465,11 +5469,10 @@ class ViewState {
5465
5469
  if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
5466
5470
  !this.viewportIsAppropriate(viewport))
5467
5471
  viewport = this.getViewport(0, scrollTarget);
5468
- let updateLines = !update.changes.empty || (update.flags & 2 /* UpdateFlag.Height */) ||
5469
- viewport.from != this.viewport.from || viewport.to != this.viewport.to;
5472
+ let viewportChange = viewport.from != this.viewport.from || viewport.to != this.viewport.to;
5470
5473
  this.viewport = viewport;
5471
- this.updateForViewport();
5472
- if (updateLines)
5474
+ update.flags |= this.updateForViewport();
5475
+ if (viewportChange || !update.changes.empty || (update.flags & 2 /* UpdateFlag.Height */))
5473
5476
  this.updateViewportLines();
5474
5477
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > (2000 /* LG.Margin */ << 1))
5475
5478
  this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
@@ -5567,9 +5570,12 @@ class ViewState {
5567
5570
  let viewportChange = !this.viewportIsAppropriate(this.viewport, bias) ||
5568
5571
  this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from ||
5569
5572
  this.scrollTarget.range.head > this.viewport.to);
5570
- if (viewportChange)
5573
+ if (viewportChange) {
5574
+ if (result & 2 /* UpdateFlag.Height */)
5575
+ result |= this.updateScaler();
5571
5576
  this.viewport = this.getViewport(bias, this.scrollTarget);
5572
- this.updateForViewport();
5577
+ result |= this.updateForViewport();
5578
+ }
5573
5579
  if ((result & 2 /* UpdateFlag.Height */) || viewportChange)
5574
5580
  this.updateViewportLines();
5575
5581
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > (2000 /* LG.Margin */ << 1))
@@ -5758,11 +5764,14 @@ class ViewState {
5758
5764
  return changed ? 4 /* UpdateFlag.Viewport */ : 0;
5759
5765
  }
5760
5766
  lineBlockAt(pos) {
5761
- return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to >= pos)) ||
5767
+ return (pos >= this.viewport.from && pos <= this.viewport.to &&
5768
+ this.viewportLines.find(b => b.from <= pos && b.to >= pos)) ||
5762
5769
  scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.heightOracle, 0, 0), this.scaler);
5763
5770
  }
5764
5771
  lineBlockAtHeight(height) {
5765
- return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
5772
+ return (height >= this.viewportLines[0].top && height <= this.viewportLines[this.viewportLines.length - 1].bottom &&
5773
+ this.viewportLines.find(l => l.top <= height && l.bottom >= height)) ||
5774
+ scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
5766
5775
  }
5767
5776
  scrollAnchorAt(scrollTop) {
5768
5777
  let block = this.lineBlockAtHeight(scrollTop + 8);
@@ -5837,7 +5846,8 @@ function find(array, f) {
5837
5846
  const IdScaler = {
5838
5847
  toDOM(n) { return n; },
5839
5848
  fromDOM(n) { return n; },
5840
- scale: 1
5849
+ scale: 1,
5850
+ eq(other) { return other == this; }
5841
5851
  };
5842
5852
  // When the height is too big (> VP.MaxDOMHeight), scale down the
5843
5853
  // regions outside the viewports so that the total height is
@@ -5880,6 +5890,12 @@ class BigScaler {
5880
5890
  domBase = vp.domBottom;
5881
5891
  }
5882
5892
  }
5893
+ eq(other) {
5894
+ if (!(other instanceof BigScaler))
5895
+ return false;
5896
+ return this.scale == other.scale && this.viewports.length == other.viewports.length &&
5897
+ this.viewports.every((vp, i) => vp.from == other.viewports[i].from && vp.to == other.viewports[i].to);
5898
+ }
5883
5899
  }
5884
5900
  function scaleBlock(block, scaler) {
5885
5901
  if (scaler.scale == 1)
@@ -6271,6 +6287,7 @@ class DOMChange {
6271
6287
  this.typeOver = typeOver;
6272
6288
  this.bounds = null;
6273
6289
  this.text = "";
6290
+ this.domChanged = start > -1;
6274
6291
  let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView;
6275
6292
  if (view.state.readOnly && start > -1) {
6276
6293
  // Ignore changes when the editor is read-only
@@ -6373,7 +6390,35 @@ function applyDOMChange(view, domChange) {
6373
6390
  change = { from: sel.from, to: sel.to, insert: state.Text.of([" "]) };
6374
6391
  }
6375
6392
  if (change) {
6376
- return applyDOMChangeInner(view, change, newSel, lastKey);
6393
+ if (browser.ios && view.inputState.flushIOSKey(change))
6394
+ return true;
6395
+ // Android browsers don't fire reasonable key events for enter,
6396
+ // backspace, or delete. So this detects changes that look like
6397
+ // they're caused by those keys, and reinterprets them as key
6398
+ // events. (Some of these keys are also handled by beforeinput
6399
+ // events and the pendingAndroidKey mechanism, but that's not
6400
+ // reliable in all situations.)
6401
+ if (browser.android &&
6402
+ ((change.to == sel.to &&
6403
+ // GBoard will sometimes remove a space it just inserted
6404
+ // after a completion when you press enter
6405
+ (change.from == sel.from || change.from == sel.from - 1 && view.state.sliceDoc(change.from, sel.from) == " ") &&
6406
+ change.insert.length == 1 && change.insert.lines == 2 &&
6407
+ dispatchKey(view.contentDOM, "Enter", 13)) ||
6408
+ ((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 ||
6409
+ lastKey == 8 && change.insert.length < change.to - change.from && change.to > sel.head) &&
6410
+ dispatchKey(view.contentDOM, "Backspace", 8)) ||
6411
+ (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
6412
+ dispatchKey(view.contentDOM, "Delete", 46))))
6413
+ return true;
6414
+ let text = change.insert.toString();
6415
+ if (view.inputState.composing >= 0)
6416
+ view.inputState.composing++;
6417
+ let defaultTr;
6418
+ let defaultInsert = () => defaultTr || (defaultTr = applyDefaultInsert(view, change, newSel));
6419
+ if (!view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text, defaultInsert)))
6420
+ view.dispatch(defaultInsert());
6421
+ return true;
6377
6422
  }
6378
6423
  else if (newSel && !newSel.main.eq(sel)) {
6379
6424
  let scrollIntoView = false, userEvent = "select";
@@ -6389,38 +6434,6 @@ function applyDOMChange(view, domChange) {
6389
6434
  return false;
6390
6435
  }
6391
6436
  }
6392
- function applyDOMChangeInner(view, change, newSel, lastKey = -1) {
6393
- if (browser.ios && view.inputState.flushIOSKey(change))
6394
- return true;
6395
- let sel = view.state.selection.main;
6396
- // Android browsers don't fire reasonable key events for enter,
6397
- // backspace, or delete. So this detects changes that look like
6398
- // they're caused by those keys, and reinterprets them as key
6399
- // events. (Some of these keys are also handled by beforeinput
6400
- // events and the pendingAndroidKey mechanism, but that's not
6401
- // reliable in all situations.)
6402
- if (browser.android &&
6403
- ((change.to == sel.to &&
6404
- // GBoard will sometimes remove a space it just inserted
6405
- // after a completion when you press enter
6406
- (change.from == sel.from || change.from == sel.from - 1 && view.state.sliceDoc(change.from, sel.from) == " ") &&
6407
- change.insert.length == 1 && change.insert.lines == 2 &&
6408
- dispatchKey(view.contentDOM, "Enter", 13)) ||
6409
- ((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 ||
6410
- lastKey == 8 && change.insert.length < change.to - change.from && change.to > sel.head) &&
6411
- dispatchKey(view.contentDOM, "Backspace", 8)) ||
6412
- (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
6413
- dispatchKey(view.contentDOM, "Delete", 46))))
6414
- return true;
6415
- let text = change.insert.toString();
6416
- if (view.inputState.composing >= 0)
6417
- view.inputState.composing++;
6418
- let defaultTr;
6419
- let defaultInsert = () => defaultTr || (defaultTr = applyDefaultInsert(view, change, newSel));
6420
- if (!view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text, defaultInsert)))
6421
- view.dispatch(defaultInsert());
6422
- return true;
6423
- }
6424
6437
  function applyDefaultInsert(view, change, newSel) {
6425
6438
  let tr, startState = view.state, sel = startState.selection.main;
6426
6439
  if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
@@ -6547,7 +6560,6 @@ class DOMObserver {
6547
6560
  constructor(view) {
6548
6561
  this.view = view;
6549
6562
  this.active = false;
6550
- this.editContext = null;
6551
6563
  // The known selection. Kept in our own object, as opposed to just
6552
6564
  // directly accessing the selection because:
6553
6565
  // - Safari doesn't report the right selection in shadow DOM
@@ -6592,10 +6604,6 @@ class DOMObserver {
6592
6604
  else
6593
6605
  this.flush();
6594
6606
  });
6595
- if (window.EditContext && view.constructor.EDIT_CONTEXT !== false) {
6596
- this.editContext = new EditContextManager(view);
6597
- view.contentDOM.editContext = this.editContext.editContext;
6598
- }
6599
6607
  if (useCharData)
6600
6608
  this.onCharData = (event) => {
6601
6609
  this.queue.push({ target: event.target,
@@ -6646,8 +6654,6 @@ class DOMObserver {
6646
6654
  onScroll(e) {
6647
6655
  if (this.intersecting)
6648
6656
  this.flush(false);
6649
- if (this.editContext)
6650
- this.view.requestMeasure(this.editContext.measureReq);
6651
6657
  this.onScrollChanged(e);
6652
6658
  }
6653
6659
  onResize() {
@@ -6906,8 +6912,9 @@ class DOMObserver {
6906
6912
  }
6907
6913
  let startState = this.view.state;
6908
6914
  let handled = applyDOMChange(this.view, domChange);
6909
- // The view wasn't updated
6910
- if (this.view.state == startState)
6915
+ // The view wasn't updated but DOM/selection changes were seen. Reset the view.
6916
+ if (this.view.state == startState &&
6917
+ (domChange.domChanged || domChange.newSel && !domChange.newSel.main.eq(this.view.state.selection.main)))
6911
6918
  this.view.update([]);
6912
6919
  return handled;
6913
6920
  }
@@ -6956,10 +6963,6 @@ class DOMObserver {
6956
6963
  win.removeEventListener("beforeprint", this.onPrint);
6957
6964
  win.document.removeEventListener("selectionchange", this.onSelectionChange);
6958
6965
  }
6959
- update(update) {
6960
- if (this.editContext)
6961
- this.editContext.update(update);
6962
- }
6963
6966
  destroy() {
6964
6967
  var _a, _b, _c;
6965
6968
  this.stop();
@@ -7019,161 +7022,6 @@ function safariSelectionRangeHack(view, selection) {
7019
7022
  view.contentDOM.removeEventListener("beforeinput", read, true);
7020
7023
  return found ? buildSelectionRangeFromRange(view, found) : null;
7021
7024
  }
7022
- class EditContextManager {
7023
- constructor(view) {
7024
- // The document window for which the text in the context is
7025
- // maintained. For large documents, this may be smaller than the
7026
- // editor document. This window always includes the selection head.
7027
- this.from = 0;
7028
- this.to = 0;
7029
- // When applying a transaction, this is used to compare the change
7030
- // made to the context content to the change in the transaction in
7031
- // order to make the minimal changes to the context (since touching
7032
- // that sometimes breaks series of multiple edits made for a single
7033
- // user action on some Android keyboards)
7034
- this.pendingContextChange = null;
7035
- this.resetRange(view.state);
7036
- let context = this.editContext = new window.EditContext({
7037
- text: view.state.doc.sliceString(this.from, this.to),
7038
- selectionStart: this.toContextPos(Math.max(this.from, Math.min(this.to, view.state.selection.main.anchor))),
7039
- selectionEnd: this.toContextPos(view.state.selection.main.head)
7040
- });
7041
- context.addEventListener("textupdate", e => {
7042
- let { anchor } = view.state.selection.main;
7043
- let change = { from: this.toEditorPos(e.updateRangeStart),
7044
- to: this.toEditorPos(e.updateRangeEnd),
7045
- insert: state.Text.of(e.text.split("\n")) };
7046
- // If the window doesn't include the anchor, assume changes
7047
- // adjacent to a side go up to the anchor.
7048
- if (change.from == this.from && anchor < this.from)
7049
- change.from = anchor;
7050
- else if (change.to == this.to && anchor > this.to)
7051
- change.to = anchor;
7052
- // Edit context sometimes fire empty changes
7053
- if (change.from == change.to && !change.insert.length)
7054
- return;
7055
- this.pendingContextChange = change;
7056
- applyDOMChangeInner(view, change, state.EditorSelection.single(this.toEditorPos(e.selectionStart), this.toEditorPos(e.selectionEnd)));
7057
- // If the transaction didn't flush our change, revert it so
7058
- // that the context is in sync with the editor state again.
7059
- if (this.pendingContextChange)
7060
- this.revertPending(view.state);
7061
- });
7062
- context.addEventListener("characterboundsupdate", e => {
7063
- let rects = [], prev = null;
7064
- for (let i = this.toEditorPos(e.rangeStart), end = this.toEditorPos(e.rangeEnd); i < end; i++) {
7065
- let rect = view.coordsForChar(i);
7066
- prev = (rect && new DOMRect(rect.left, rect.right, rect.right - rect.left, rect.bottom - rect.top))
7067
- || prev || new DOMRect;
7068
- rects.push(prev);
7069
- }
7070
- context.updateCharacterBounds(e.rangeStart, rects);
7071
- });
7072
- context.addEventListener("textformatupdate", e => {
7073
- let deco = [];
7074
- for (let format of e.getTextFormats()) {
7075
- let lineStyle = format.underlineStyle, thickness = format.underlineThickness;
7076
- if (lineStyle != "None" && thickness != "None") {
7077
- let style = `text-decoration: underline ${lineStyle == "Dashed" ? "dashed " : lineStyle == "Squiggle" ? "wavy " : ""}${thickness == "Thin" ? 1 : 2}px`;
7078
- deco.push(Decoration.mark({ attributes: { style } })
7079
- .range(this.toEditorPos(format.rangeStart), this.toEditorPos(format.rangeEnd)));
7080
- }
7081
- }
7082
- view.dispatch({ effects: setEditContextFormatting.of(Decoration.set(deco)) });
7083
- });
7084
- context.addEventListener("compositionstart", () => {
7085
- if (view.inputState.composing < 0) {
7086
- view.inputState.composing = 0;
7087
- view.inputState.compositionFirstChange = true;
7088
- }
7089
- });
7090
- context.addEventListener("compositionend", () => {
7091
- view.inputState.composing = -1;
7092
- view.inputState.compositionFirstChange = null;
7093
- });
7094
- this.measureReq = { read: view => {
7095
- this.editContext.updateControlBounds(view.contentDOM.getBoundingClientRect());
7096
- let sel = getSelection(view.root);
7097
- if (sel && sel.rangeCount)
7098
- this.editContext.updateSelectionBounds(sel.getRangeAt(0).getBoundingClientRect());
7099
- } };
7100
- }
7101
- applyEdits(update) {
7102
- let off = 0, abort = false, pending = this.pendingContextChange;
7103
- update.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => {
7104
- if (abort)
7105
- return;
7106
- let dLen = insert.length - (toA - fromA);
7107
- if (pending && toA >= pending.to) {
7108
- if (pending.from == fromA && pending.to == toA && pending.insert.eq(insert)) {
7109
- pending = this.pendingContextChange = null; // Match
7110
- off += dLen;
7111
- return;
7112
- }
7113
- else { // Mismatch, revert
7114
- pending = null;
7115
- this.revertPending(update.state);
7116
- }
7117
- }
7118
- fromA += off;
7119
- toA += off;
7120
- if (toA <= this.from) { // Before the window
7121
- this.from += dLen;
7122
- this.to += dLen;
7123
- }
7124
- else if (fromA < this.to) { // Overlaps with window
7125
- if (fromA < this.from || toA > this.to || (this.to - this.from) + insert.length > 30000 /* CxVp.MaxSize */) {
7126
- abort = true;
7127
- return;
7128
- }
7129
- this.editContext.updateText(this.toContextPos(fromA), this.toContextPos(toA), insert.toString());
7130
- this.to += dLen;
7131
- }
7132
- off += dLen;
7133
- });
7134
- if (pending && !abort)
7135
- this.revertPending(update.state);
7136
- return !abort;
7137
- }
7138
- update(update) {
7139
- if (!this.applyEdits(update) || !this.rangeIsValid(update.state)) {
7140
- this.pendingContextChange = null;
7141
- this.resetRange(update.state);
7142
- this.editContext.updateText(0, this.editContext.text.length, update.state.doc.sliceString(this.from, this.to));
7143
- this.setSelection(update.state);
7144
- }
7145
- else if (update.docChanged || update.selectionSet) {
7146
- this.setSelection(update.state);
7147
- }
7148
- if (update.geometryChanged || update.docChanged || update.selectionSet)
7149
- update.view.requestMeasure(this.measureReq);
7150
- }
7151
- resetRange(state) {
7152
- let { head } = state.selection.main;
7153
- this.from = Math.max(0, head - 10000 /* CxVp.Margin */);
7154
- this.to = Math.min(state.doc.length, head + 10000 /* CxVp.Margin */);
7155
- }
7156
- revertPending(state) {
7157
- let pending = this.pendingContextChange;
7158
- this.pendingContextChange = null;
7159
- this.editContext.updateText(this.toContextPos(pending.from), this.toContextPos(pending.to + pending.insert.length), state.doc.sliceString(pending.from, pending.to));
7160
- }
7161
- setSelection(state) {
7162
- let { main } = state.selection;
7163
- let start = this.toContextPos(Math.max(this.from, Math.min(this.to, main.anchor)));
7164
- let end = this.toContextPos(main.head);
7165
- if (this.editContext.selectionStart != start || this.editContext.selectionEnd != end)
7166
- this.editContext.updateSelection(start, end);
7167
- }
7168
- rangeIsValid(state) {
7169
- let { head } = state.selection.main;
7170
- return !(this.from > 0 && head - this.from < 500 /* CxVp.MinMargin */ ||
7171
- this.to < state.doc.length && this.to - head < 500 /* CxVp.MinMargin */ ||
7172
- this.to - this.from > 10000 /* CxVp.Margin */ * 3);
7173
- }
7174
- toEditorPos(contextPos) { return contextPos + this.from; }
7175
- toContextPos(editorPos) { return editorPos - this.from; }
7176
- }
7177
7025
 
7178
7026
  // The editor's update state machine looks something like this:
7179
7027
  //
@@ -8604,11 +8452,17 @@ function getBase(view) {
8604
8452
  let left = view.textDirection == exports.Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth * view.scaleX;
8605
8453
  return { left: left - view.scrollDOM.scrollLeft * view.scaleX, top: rect.top - view.scrollDOM.scrollTop * view.scaleY };
8606
8454
  }
8607
- function wrappedLine(view, pos, inside) {
8608
- let range = state.EditorSelection.cursor(pos);
8609
- return { from: Math.max(inside.from, view.moveToLineBoundary(range, false, true).from),
8610
- to: Math.min(inside.to, view.moveToLineBoundary(range, true, true).from),
8611
- type: exports.BlockType.Text };
8455
+ function wrappedLine(view, pos, side, inside) {
8456
+ let coords = view.coordsAtPos(pos, side * 2);
8457
+ if (!coords)
8458
+ return inside;
8459
+ let editorRect = view.dom.getBoundingClientRect();
8460
+ let y = (coords.top + coords.bottom) / 2;
8461
+ let left = view.posAtCoords({ x: editorRect.left + 1, y });
8462
+ let right = view.posAtCoords({ x: editorRect.right - 1, y });
8463
+ if (left == null || right == null)
8464
+ return inside;
8465
+ return { from: Math.max(inside.from, Math.min(left, right)), to: Math.min(inside.to, Math.max(left, right)) };
8612
8466
  }
8613
8467
  function rectanglesForRange(view, className, range) {
8614
8468
  if (range.to <= view.viewport.from || range.from >= view.viewport.to)
@@ -8624,10 +8478,10 @@ function rectanglesForRange(view, className, range) {
8624
8478
  let visualStart = startBlock.type == exports.BlockType.Text ? startBlock : null;
8625
8479
  let visualEnd = endBlock.type == exports.BlockType.Text ? endBlock : null;
8626
8480
  if (visualStart && (view.lineWrapping || startBlock.widgetLineBreaks))
8627
- visualStart = wrappedLine(view, from, visualStart);
8481
+ visualStart = wrappedLine(view, from, 1, visualStart);
8628
8482
  if (visualEnd && (view.lineWrapping || endBlock.widgetLineBreaks))
8629
- visualEnd = wrappedLine(view, to, visualEnd);
8630
- if (visualStart && visualEnd && visualStart.from == visualEnd.from) {
8483
+ visualEnd = wrappedLine(view, to, -1, visualEnd);
8484
+ if (visualStart && visualEnd && visualStart.from == visualEnd.from && visualStart.to == visualEnd.to) {
8631
8485
  return pieces(drawForLine(range.from, range.to, visualStart));
8632
8486
  }
8633
8487
  else {
@@ -8882,14 +8736,19 @@ const selectionLayer = layer({
8882
8736
  });
8883
8737
  const themeSpec = {
8884
8738
  ".cm-line": {
8885
- "& ::selection": { backgroundColor: "transparent !important" },
8886
- "&::selection": { backgroundColor: "transparent !important" }
8739
+ "& ::selection, &::selection": { backgroundColor: "transparent !important" },
8740
+ },
8741
+ ".cm-content": {
8742
+ "& :focus": {
8743
+ caretColor: "initial !important",
8744
+ "&::selection, & ::selection": {
8745
+ backgroundColor: "Highlight !important"
8746
+ }
8747
+ }
8887
8748
  }
8888
8749
  };
8889
- if (CanHidePrimary) {
8890
- themeSpec[".cm-line"].caretColor = "transparent !important";
8891
- themeSpec[".cm-content"] = { caretColor: "transparent !important" };
8892
- }
8750
+ if (CanHidePrimary)
8751
+ themeSpec[".cm-line"].caretColor = themeSpec[".cm-content"].caretColor = "transparent !important";
8893
8752
  const hideNativeSelection = state.Prec.highest(EditorView.theme(themeSpec));
8894
8753
 
8895
8754
  const setDropCursorPos = state.StateEffect.define({
package/dist/index.js CHANGED
@@ -743,7 +743,7 @@ var browser = {
743
743
  android: /*@__PURE__*//Android\b/.test(nav.userAgent),
744
744
  webkit,
745
745
  safari,
746
- webkit_version: webkit ? +(/*@__PURE__*//\bAppleWebKit\/(\d+)/.exec(navigator.userAgent) || [0, 0])[1] : 0,
746
+ webkit_version: webkit ? +(/*@__PURE__*//\bAppleWebKit\/(\d+)/.exec(nav.userAgent) || [0, 0])[1] : 0,
747
747
  tabSize: doc.documentElement.style.tabSize != null ? "tab-size" : "-moz-tab-size"
748
748
  };
749
749
 
@@ -2364,7 +2364,6 @@ class ScrollTarget {
2364
2364
  }
2365
2365
  }
2366
2366
  const scrollIntoView = /*@__PURE__*/StateEffect.define({ map: (t, ch) => t.map(ch) });
2367
- const setEditContextFormatting = /*@__PURE__*/StateEffect.define();
2368
2367
  /**
2369
2368
  Log or report an unhandled exception in client code. Should
2370
2369
  probably only be used by extension code that allows client code to
@@ -2701,11 +2700,10 @@ class DocView extends ContentView {
2701
2700
  super();
2702
2701
  this.view = view;
2703
2702
  this.decorations = [];
2704
- this.dynamicDecorationMap = [false];
2703
+ this.dynamicDecorationMap = [];
2705
2704
  this.domChanged = null;
2706
2705
  this.hasComposition = null;
2707
2706
  this.markedForComposition = new Set;
2708
- this.editContextFormatting = Decoration.none;
2709
2707
  this.lastCompositionAfterCursor = false;
2710
2708
  // Track a minimum width for the editor. When measuring sizes in
2711
2709
  // measureVisibleLineHeights, this is updated to point at the width
@@ -2744,9 +2742,8 @@ class DocView extends ContentView {
2744
2742
  this.minWidthTo = update.changes.mapPos(this.minWidthTo, 1);
2745
2743
  }
2746
2744
  }
2747
- this.updateEditContextFormatting(update);
2748
2745
  let readCompositionAt = -1;
2749
- if (this.view.inputState.composing >= 0 && !this.view.observer.editContext) {
2746
+ if (this.view.inputState.composing >= 0) {
2750
2747
  if ((_a = this.domChanged) === null || _a === void 0 ? void 0 : _a.newSel)
2751
2748
  readCompositionAt = this.domChanged.newSel.head;
2752
2749
  else if (!touchesComposition(update.changes, this.hasComposition) && !update.selectionSet)
@@ -2854,14 +2851,6 @@ class DocView extends ContentView {
2854
2851
  if (composition)
2855
2852
  this.fixCompositionDOM(composition);
2856
2853
  }
2857
- updateEditContextFormatting(update) {
2858
- this.editContextFormatting = this.editContextFormatting.map(update.changes);
2859
- for (let tr of update.transactions)
2860
- for (let effect of tr.effects)
2861
- if (effect.is(setEditContextFormatting)) {
2862
- this.editContextFormatting = effect.value;
2863
- }
2864
- }
2865
2854
  compositionView(composition) {
2866
2855
  let cur = new TextView(composition.text.nodeValue);
2867
2856
  cur.flags |= 8 /* ViewFlag.Composition */;
@@ -3067,6 +3056,12 @@ class DocView extends ContentView {
3067
3056
  best = child;
3068
3057
  bestPos = start;
3069
3058
  }
3059
+ else if (best && start == pos && end == pos && child instanceof BlockWidgetView && Math.abs(side) < 2) {
3060
+ if (child.deco.startSide < 0)
3061
+ break;
3062
+ else if (i)
3063
+ best = null;
3064
+ }
3070
3065
  off = start;
3071
3066
  }
3072
3067
  return best ? best.coordsAt(pos - bestPos, side) : null;
@@ -3187,7 +3182,7 @@ class DocView extends ContentView {
3187
3182
  return Decoration.set(deco);
3188
3183
  }
3189
3184
  updateDeco() {
3190
- let i = 1;
3185
+ let i = 0;
3191
3186
  let allDeco = this.view.state.facet(decorations).map(d => {
3192
3187
  let dynamic = this.dynamicDecorationMap[i++] = typeof d == "function";
3193
3188
  return dynamic ? d(this.view) : d;
@@ -3203,7 +3198,6 @@ class DocView extends ContentView {
3203
3198
  allDeco.push(RangeSet.join(outerDeco));
3204
3199
  }
3205
3200
  this.decorations = [
3206
- this.editContextFormatting,
3207
3201
  ...allDeco,
3208
3202
  this.computeBlockGapDeco(),
3209
3203
  this.view.viewState.lineGapDeco
@@ -3890,7 +3884,6 @@ class InputState {
3890
3884
  this.mouseSelection = mouseSelection;
3891
3885
  }
3892
3886
  update(update) {
3893
- this.view.observer.update(update);
3894
3887
  if (this.mouseSelection)
3895
3888
  this.mouseSelection.update(update);
3896
3889
  if (this.draggedContent && update.docChanged)
@@ -4069,7 +4062,9 @@ class MouseSelection {
4069
4062
  this.mustSelect = false;
4070
4063
  }
4071
4064
  update(update) {
4072
- if (this.style.update(update))
4065
+ if (update.transactions.some(tr => tr.isUserEvent("input.type")))
4066
+ this.destroy();
4067
+ else if (this.style.update(update))
4073
4068
  setTimeout(() => this.select(this.lastEvent), 20);
4074
4069
  }
4075
4070
  }
@@ -4193,7 +4188,12 @@ handlers.mousedown = (view, event) => {
4193
4188
  let mustFocus = !view.hasFocus;
4194
4189
  view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
4195
4190
  if (mustFocus)
4196
- view.observer.ignore(() => focusPreventScroll(view.contentDOM));
4191
+ view.observer.ignore(() => {
4192
+ focusPreventScroll(view.contentDOM);
4193
+ let active = view.root.activeElement;
4194
+ if (active && !active.contains(view.contentDOM))
4195
+ active.blur();
4196
+ });
4197
4197
  let mouseSel = view.inputState.mouseSelection;
4198
4198
  if (mouseSel) {
4199
4199
  mouseSel.start(event);
@@ -4481,8 +4481,6 @@ observers.blur = view => {
4481
4481
  updateForFocusChange(view);
4482
4482
  };
4483
4483
  observers.compositionstart = observers.compositionupdate = view => {
4484
- if (view.observer.editContext)
4485
- return; // Composition handled by edit context
4486
4484
  if (view.inputState.compositionFirstChange == null)
4487
4485
  view.inputState.compositionFirstChange = true;
4488
4486
  if (view.inputState.composing < 0) {
@@ -4491,8 +4489,6 @@ observers.compositionstart = observers.compositionupdate = view => {
4491
4489
  }
4492
4490
  };
4493
4491
  observers.compositionend = view => {
4494
- if (view.observer.editContext)
4495
- return; // Composition handled by edit context
4496
4492
  view.inputState.composing = -1;
4497
4493
  view.inputState.compositionEndedAt = Date.now();
4498
4494
  view.inputState.compositionPendingKey = true;
@@ -5411,9 +5407,12 @@ class ViewState {
5411
5407
  this.heightOracle = new HeightOracle(guessWrapping);
5412
5408
  this.stateDeco = state.facet(decorations).filter(d => typeof d != "function");
5413
5409
  this.heightMap = HeightMap.empty().applyChanges(this.stateDeco, Text.empty, this.heightOracle.setDoc(state.doc), [new ChangedRange(0, 0, 0, state.doc.length)]);
5414
- this.viewport = this.getViewport(0, null);
5410
+ for (let i = 0; i < 2; i++) {
5411
+ this.viewport = this.getViewport(0, null);
5412
+ if (!this.updateForViewport())
5413
+ break;
5414
+ }
5415
5415
  this.updateViewportLines();
5416
- this.updateForViewport();
5417
5416
  this.lineGaps = this.ensureLineGaps([]);
5418
5417
  this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(this, false)));
5419
5418
  this.computeVisibleRanges();
@@ -5428,13 +5427,18 @@ class ViewState {
5428
5427
  }
5429
5428
  }
5430
5429
  this.viewports = viewports.sort((a, b) => a.from - b.from);
5430
+ return this.updateScaler();
5431
+ }
5432
+ updateScaler() {
5433
+ let scaler = this.scaler;
5431
5434
  this.scaler = this.heightMap.height <= 7000000 /* VP.MaxDOMHeight */ ? IdScaler :
5432
5435
  new BigScaler(this.heightOracle, this.heightMap, this.viewports);
5436
+ return scaler.eq(this.scaler) ? 0 : 2 /* UpdateFlag.Height */;
5433
5437
  }
5434
5438
  updateViewportLines() {
5435
5439
  this.viewportLines = [];
5436
5440
  this.heightMap.forEachLine(this.viewport.from, this.viewport.to, this.heightOracle.setDoc(this.state.doc), 0, 0, block => {
5437
- this.viewportLines.push(this.scaler.scale == 1 ? block : scaleBlock(block, this.scaler));
5441
+ this.viewportLines.push(scaleBlock(block, this.scaler));
5438
5442
  });
5439
5443
  }
5440
5444
  update(update, scrollTarget = null) {
@@ -5460,11 +5464,10 @@ class ViewState {
5460
5464
  if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
5461
5465
  !this.viewportIsAppropriate(viewport))
5462
5466
  viewport = this.getViewport(0, scrollTarget);
5463
- let updateLines = !update.changes.empty || (update.flags & 2 /* UpdateFlag.Height */) ||
5464
- viewport.from != this.viewport.from || viewport.to != this.viewport.to;
5467
+ let viewportChange = viewport.from != this.viewport.from || viewport.to != this.viewport.to;
5465
5468
  this.viewport = viewport;
5466
- this.updateForViewport();
5467
- if (updateLines)
5469
+ update.flags |= this.updateForViewport();
5470
+ if (viewportChange || !update.changes.empty || (update.flags & 2 /* UpdateFlag.Height */))
5468
5471
  this.updateViewportLines();
5469
5472
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > (2000 /* LG.Margin */ << 1))
5470
5473
  this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
@@ -5562,9 +5565,12 @@ class ViewState {
5562
5565
  let viewportChange = !this.viewportIsAppropriate(this.viewport, bias) ||
5563
5566
  this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from ||
5564
5567
  this.scrollTarget.range.head > this.viewport.to);
5565
- if (viewportChange)
5568
+ if (viewportChange) {
5569
+ if (result & 2 /* UpdateFlag.Height */)
5570
+ result |= this.updateScaler();
5566
5571
  this.viewport = this.getViewport(bias, this.scrollTarget);
5567
- this.updateForViewport();
5572
+ result |= this.updateForViewport();
5573
+ }
5568
5574
  if ((result & 2 /* UpdateFlag.Height */) || viewportChange)
5569
5575
  this.updateViewportLines();
5570
5576
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > (2000 /* LG.Margin */ << 1))
@@ -5753,11 +5759,14 @@ class ViewState {
5753
5759
  return changed ? 4 /* UpdateFlag.Viewport */ : 0;
5754
5760
  }
5755
5761
  lineBlockAt(pos) {
5756
- return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to >= pos)) ||
5762
+ return (pos >= this.viewport.from && pos <= this.viewport.to &&
5763
+ this.viewportLines.find(b => b.from <= pos && b.to >= pos)) ||
5757
5764
  scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.heightOracle, 0, 0), this.scaler);
5758
5765
  }
5759
5766
  lineBlockAtHeight(height) {
5760
- return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
5767
+ return (height >= this.viewportLines[0].top && height <= this.viewportLines[this.viewportLines.length - 1].bottom &&
5768
+ this.viewportLines.find(l => l.top <= height && l.bottom >= height)) ||
5769
+ scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
5761
5770
  }
5762
5771
  scrollAnchorAt(scrollTop) {
5763
5772
  let block = this.lineBlockAtHeight(scrollTop + 8);
@@ -5832,7 +5841,8 @@ function find(array, f) {
5832
5841
  const IdScaler = {
5833
5842
  toDOM(n) { return n; },
5834
5843
  fromDOM(n) { return n; },
5835
- scale: 1
5844
+ scale: 1,
5845
+ eq(other) { return other == this; }
5836
5846
  };
5837
5847
  // When the height is too big (> VP.MaxDOMHeight), scale down the
5838
5848
  // regions outside the viewports so that the total height is
@@ -5875,6 +5885,12 @@ class BigScaler {
5875
5885
  domBase = vp.domBottom;
5876
5886
  }
5877
5887
  }
5888
+ eq(other) {
5889
+ if (!(other instanceof BigScaler))
5890
+ return false;
5891
+ return this.scale == other.scale && this.viewports.length == other.viewports.length &&
5892
+ this.viewports.every((vp, i) => vp.from == other.viewports[i].from && vp.to == other.viewports[i].to);
5893
+ }
5878
5894
  }
5879
5895
  function scaleBlock(block, scaler) {
5880
5896
  if (scaler.scale == 1)
@@ -6266,6 +6282,7 @@ class DOMChange {
6266
6282
  this.typeOver = typeOver;
6267
6283
  this.bounds = null;
6268
6284
  this.text = "";
6285
+ this.domChanged = start > -1;
6269
6286
  let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView;
6270
6287
  if (view.state.readOnly && start > -1) {
6271
6288
  // Ignore changes when the editor is read-only
@@ -6368,7 +6385,35 @@ function applyDOMChange(view, domChange) {
6368
6385
  change = { from: sel.from, to: sel.to, insert: Text.of([" "]) };
6369
6386
  }
6370
6387
  if (change) {
6371
- return applyDOMChangeInner(view, change, newSel, lastKey);
6388
+ if (browser.ios && view.inputState.flushIOSKey(change))
6389
+ return true;
6390
+ // Android browsers don't fire reasonable key events for enter,
6391
+ // backspace, or delete. So this detects changes that look like
6392
+ // they're caused by those keys, and reinterprets them as key
6393
+ // events. (Some of these keys are also handled by beforeinput
6394
+ // events and the pendingAndroidKey mechanism, but that's not
6395
+ // reliable in all situations.)
6396
+ if (browser.android &&
6397
+ ((change.to == sel.to &&
6398
+ // GBoard will sometimes remove a space it just inserted
6399
+ // after a completion when you press enter
6400
+ (change.from == sel.from || change.from == sel.from - 1 && view.state.sliceDoc(change.from, sel.from) == " ") &&
6401
+ change.insert.length == 1 && change.insert.lines == 2 &&
6402
+ dispatchKey(view.contentDOM, "Enter", 13)) ||
6403
+ ((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 ||
6404
+ lastKey == 8 && change.insert.length < change.to - change.from && change.to > sel.head) &&
6405
+ dispatchKey(view.contentDOM, "Backspace", 8)) ||
6406
+ (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
6407
+ dispatchKey(view.contentDOM, "Delete", 46))))
6408
+ return true;
6409
+ let text = change.insert.toString();
6410
+ if (view.inputState.composing >= 0)
6411
+ view.inputState.composing++;
6412
+ let defaultTr;
6413
+ let defaultInsert = () => defaultTr || (defaultTr = applyDefaultInsert(view, change, newSel));
6414
+ if (!view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text, defaultInsert)))
6415
+ view.dispatch(defaultInsert());
6416
+ return true;
6372
6417
  }
6373
6418
  else if (newSel && !newSel.main.eq(sel)) {
6374
6419
  let scrollIntoView = false, userEvent = "select";
@@ -6384,38 +6429,6 @@ function applyDOMChange(view, domChange) {
6384
6429
  return false;
6385
6430
  }
6386
6431
  }
6387
- function applyDOMChangeInner(view, change, newSel, lastKey = -1) {
6388
- if (browser.ios && view.inputState.flushIOSKey(change))
6389
- return true;
6390
- let sel = view.state.selection.main;
6391
- // Android browsers don't fire reasonable key events for enter,
6392
- // backspace, or delete. So this detects changes that look like
6393
- // they're caused by those keys, and reinterprets them as key
6394
- // events. (Some of these keys are also handled by beforeinput
6395
- // events and the pendingAndroidKey mechanism, but that's not
6396
- // reliable in all situations.)
6397
- if (browser.android &&
6398
- ((change.to == sel.to &&
6399
- // GBoard will sometimes remove a space it just inserted
6400
- // after a completion when you press enter
6401
- (change.from == sel.from || change.from == sel.from - 1 && view.state.sliceDoc(change.from, sel.from) == " ") &&
6402
- change.insert.length == 1 && change.insert.lines == 2 &&
6403
- dispatchKey(view.contentDOM, "Enter", 13)) ||
6404
- ((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 ||
6405
- lastKey == 8 && change.insert.length < change.to - change.from && change.to > sel.head) &&
6406
- dispatchKey(view.contentDOM, "Backspace", 8)) ||
6407
- (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
6408
- dispatchKey(view.contentDOM, "Delete", 46))))
6409
- return true;
6410
- let text = change.insert.toString();
6411
- if (view.inputState.composing >= 0)
6412
- view.inputState.composing++;
6413
- let defaultTr;
6414
- let defaultInsert = () => defaultTr || (defaultTr = applyDefaultInsert(view, change, newSel));
6415
- if (!view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text, defaultInsert)))
6416
- view.dispatch(defaultInsert());
6417
- return true;
6418
- }
6419
6432
  function applyDefaultInsert(view, change, newSel) {
6420
6433
  let tr, startState = view.state, sel = startState.selection.main;
6421
6434
  if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
@@ -6542,7 +6555,6 @@ class DOMObserver {
6542
6555
  constructor(view) {
6543
6556
  this.view = view;
6544
6557
  this.active = false;
6545
- this.editContext = null;
6546
6558
  // The known selection. Kept in our own object, as opposed to just
6547
6559
  // directly accessing the selection because:
6548
6560
  // - Safari doesn't report the right selection in shadow DOM
@@ -6587,10 +6599,6 @@ class DOMObserver {
6587
6599
  else
6588
6600
  this.flush();
6589
6601
  });
6590
- if (window.EditContext && view.constructor.EDIT_CONTEXT !== false) {
6591
- this.editContext = new EditContextManager(view);
6592
- view.contentDOM.editContext = this.editContext.editContext;
6593
- }
6594
6602
  if (useCharData)
6595
6603
  this.onCharData = (event) => {
6596
6604
  this.queue.push({ target: event.target,
@@ -6641,8 +6649,6 @@ class DOMObserver {
6641
6649
  onScroll(e) {
6642
6650
  if (this.intersecting)
6643
6651
  this.flush(false);
6644
- if (this.editContext)
6645
- this.view.requestMeasure(this.editContext.measureReq);
6646
6652
  this.onScrollChanged(e);
6647
6653
  }
6648
6654
  onResize() {
@@ -6901,8 +6907,9 @@ class DOMObserver {
6901
6907
  }
6902
6908
  let startState = this.view.state;
6903
6909
  let handled = applyDOMChange(this.view, domChange);
6904
- // The view wasn't updated
6905
- if (this.view.state == startState)
6910
+ // The view wasn't updated but DOM/selection changes were seen. Reset the view.
6911
+ if (this.view.state == startState &&
6912
+ (domChange.domChanged || domChange.newSel && !domChange.newSel.main.eq(this.view.state.selection.main)))
6906
6913
  this.view.update([]);
6907
6914
  return handled;
6908
6915
  }
@@ -6951,10 +6958,6 @@ class DOMObserver {
6951
6958
  win.removeEventListener("beforeprint", this.onPrint);
6952
6959
  win.document.removeEventListener("selectionchange", this.onSelectionChange);
6953
6960
  }
6954
- update(update) {
6955
- if (this.editContext)
6956
- this.editContext.update(update);
6957
- }
6958
6961
  destroy() {
6959
6962
  var _a, _b, _c;
6960
6963
  this.stop();
@@ -7014,161 +7017,6 @@ function safariSelectionRangeHack(view, selection) {
7014
7017
  view.contentDOM.removeEventListener("beforeinput", read, true);
7015
7018
  return found ? buildSelectionRangeFromRange(view, found) : null;
7016
7019
  }
7017
- class EditContextManager {
7018
- constructor(view) {
7019
- // The document window for which the text in the context is
7020
- // maintained. For large documents, this may be smaller than the
7021
- // editor document. This window always includes the selection head.
7022
- this.from = 0;
7023
- this.to = 0;
7024
- // When applying a transaction, this is used to compare the change
7025
- // made to the context content to the change in the transaction in
7026
- // order to make the minimal changes to the context (since touching
7027
- // that sometimes breaks series of multiple edits made for a single
7028
- // user action on some Android keyboards)
7029
- this.pendingContextChange = null;
7030
- this.resetRange(view.state);
7031
- let context = this.editContext = new window.EditContext({
7032
- text: view.state.doc.sliceString(this.from, this.to),
7033
- selectionStart: this.toContextPos(Math.max(this.from, Math.min(this.to, view.state.selection.main.anchor))),
7034
- selectionEnd: this.toContextPos(view.state.selection.main.head)
7035
- });
7036
- context.addEventListener("textupdate", e => {
7037
- let { anchor } = view.state.selection.main;
7038
- let change = { from: this.toEditorPos(e.updateRangeStart),
7039
- to: this.toEditorPos(e.updateRangeEnd),
7040
- insert: Text.of(e.text.split("\n")) };
7041
- // If the window doesn't include the anchor, assume changes
7042
- // adjacent to a side go up to the anchor.
7043
- if (change.from == this.from && anchor < this.from)
7044
- change.from = anchor;
7045
- else if (change.to == this.to && anchor > this.to)
7046
- change.to = anchor;
7047
- // Edit context sometimes fire empty changes
7048
- if (change.from == change.to && !change.insert.length)
7049
- return;
7050
- this.pendingContextChange = change;
7051
- applyDOMChangeInner(view, change, EditorSelection.single(this.toEditorPos(e.selectionStart), this.toEditorPos(e.selectionEnd)));
7052
- // If the transaction didn't flush our change, revert it so
7053
- // that the context is in sync with the editor state again.
7054
- if (this.pendingContextChange)
7055
- this.revertPending(view.state);
7056
- });
7057
- context.addEventListener("characterboundsupdate", e => {
7058
- let rects = [], prev = null;
7059
- for (let i = this.toEditorPos(e.rangeStart), end = this.toEditorPos(e.rangeEnd); i < end; i++) {
7060
- let rect = view.coordsForChar(i);
7061
- prev = (rect && new DOMRect(rect.left, rect.right, rect.right - rect.left, rect.bottom - rect.top))
7062
- || prev || new DOMRect;
7063
- rects.push(prev);
7064
- }
7065
- context.updateCharacterBounds(e.rangeStart, rects);
7066
- });
7067
- context.addEventListener("textformatupdate", e => {
7068
- let deco = [];
7069
- for (let format of e.getTextFormats()) {
7070
- let lineStyle = format.underlineStyle, thickness = format.underlineThickness;
7071
- if (lineStyle != "None" && thickness != "None") {
7072
- let style = `text-decoration: underline ${lineStyle == "Dashed" ? "dashed " : lineStyle == "Squiggle" ? "wavy " : ""}${thickness == "Thin" ? 1 : 2}px`;
7073
- deco.push(Decoration.mark({ attributes: { style } })
7074
- .range(this.toEditorPos(format.rangeStart), this.toEditorPos(format.rangeEnd)));
7075
- }
7076
- }
7077
- view.dispatch({ effects: setEditContextFormatting.of(Decoration.set(deco)) });
7078
- });
7079
- context.addEventListener("compositionstart", () => {
7080
- if (view.inputState.composing < 0) {
7081
- view.inputState.composing = 0;
7082
- view.inputState.compositionFirstChange = true;
7083
- }
7084
- });
7085
- context.addEventListener("compositionend", () => {
7086
- view.inputState.composing = -1;
7087
- view.inputState.compositionFirstChange = null;
7088
- });
7089
- this.measureReq = { read: view => {
7090
- this.editContext.updateControlBounds(view.contentDOM.getBoundingClientRect());
7091
- let sel = getSelection(view.root);
7092
- if (sel && sel.rangeCount)
7093
- this.editContext.updateSelectionBounds(sel.getRangeAt(0).getBoundingClientRect());
7094
- } };
7095
- }
7096
- applyEdits(update) {
7097
- let off = 0, abort = false, pending = this.pendingContextChange;
7098
- update.changes.iterChanges((fromA, toA, _fromB, _toB, insert) => {
7099
- if (abort)
7100
- return;
7101
- let dLen = insert.length - (toA - fromA);
7102
- if (pending && toA >= pending.to) {
7103
- if (pending.from == fromA && pending.to == toA && pending.insert.eq(insert)) {
7104
- pending = this.pendingContextChange = null; // Match
7105
- off += dLen;
7106
- return;
7107
- }
7108
- else { // Mismatch, revert
7109
- pending = null;
7110
- this.revertPending(update.state);
7111
- }
7112
- }
7113
- fromA += off;
7114
- toA += off;
7115
- if (toA <= this.from) { // Before the window
7116
- this.from += dLen;
7117
- this.to += dLen;
7118
- }
7119
- else if (fromA < this.to) { // Overlaps with window
7120
- if (fromA < this.from || toA > this.to || (this.to - this.from) + insert.length > 30000 /* CxVp.MaxSize */) {
7121
- abort = true;
7122
- return;
7123
- }
7124
- this.editContext.updateText(this.toContextPos(fromA), this.toContextPos(toA), insert.toString());
7125
- this.to += dLen;
7126
- }
7127
- off += dLen;
7128
- });
7129
- if (pending && !abort)
7130
- this.revertPending(update.state);
7131
- return !abort;
7132
- }
7133
- update(update) {
7134
- if (!this.applyEdits(update) || !this.rangeIsValid(update.state)) {
7135
- this.pendingContextChange = null;
7136
- this.resetRange(update.state);
7137
- this.editContext.updateText(0, this.editContext.text.length, update.state.doc.sliceString(this.from, this.to));
7138
- this.setSelection(update.state);
7139
- }
7140
- else if (update.docChanged || update.selectionSet) {
7141
- this.setSelection(update.state);
7142
- }
7143
- if (update.geometryChanged || update.docChanged || update.selectionSet)
7144
- update.view.requestMeasure(this.measureReq);
7145
- }
7146
- resetRange(state) {
7147
- let { head } = state.selection.main;
7148
- this.from = Math.max(0, head - 10000 /* CxVp.Margin */);
7149
- this.to = Math.min(state.doc.length, head + 10000 /* CxVp.Margin */);
7150
- }
7151
- revertPending(state) {
7152
- let pending = this.pendingContextChange;
7153
- this.pendingContextChange = null;
7154
- this.editContext.updateText(this.toContextPos(pending.from), this.toContextPos(pending.to + pending.insert.length), state.doc.sliceString(pending.from, pending.to));
7155
- }
7156
- setSelection(state) {
7157
- let { main } = state.selection;
7158
- let start = this.toContextPos(Math.max(this.from, Math.min(this.to, main.anchor)));
7159
- let end = this.toContextPos(main.head);
7160
- if (this.editContext.selectionStart != start || this.editContext.selectionEnd != end)
7161
- this.editContext.updateSelection(start, end);
7162
- }
7163
- rangeIsValid(state) {
7164
- let { head } = state.selection.main;
7165
- return !(this.from > 0 && head - this.from < 500 /* CxVp.MinMargin */ ||
7166
- this.to < state.doc.length && this.to - head < 500 /* CxVp.MinMargin */ ||
7167
- this.to - this.from > 10000 /* CxVp.Margin */ * 3);
7168
- }
7169
- toEditorPos(contextPos) { return contextPos + this.from; }
7170
- toContextPos(editorPos) { return editorPos - this.from; }
7171
- }
7172
7020
 
7173
7021
  // The editor's update state machine looks something like this:
7174
7022
  //
@@ -8599,11 +8447,17 @@ function getBase(view) {
8599
8447
  let left = view.textDirection == Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth * view.scaleX;
8600
8448
  return { left: left - view.scrollDOM.scrollLeft * view.scaleX, top: rect.top - view.scrollDOM.scrollTop * view.scaleY };
8601
8449
  }
8602
- function wrappedLine(view, pos, inside) {
8603
- let range = EditorSelection.cursor(pos);
8604
- return { from: Math.max(inside.from, view.moveToLineBoundary(range, false, true).from),
8605
- to: Math.min(inside.to, view.moveToLineBoundary(range, true, true).from),
8606
- type: BlockType.Text };
8450
+ function wrappedLine(view, pos, side, inside) {
8451
+ let coords = view.coordsAtPos(pos, side * 2);
8452
+ if (!coords)
8453
+ return inside;
8454
+ let editorRect = view.dom.getBoundingClientRect();
8455
+ let y = (coords.top + coords.bottom) / 2;
8456
+ let left = view.posAtCoords({ x: editorRect.left + 1, y });
8457
+ let right = view.posAtCoords({ x: editorRect.right - 1, y });
8458
+ if (left == null || right == null)
8459
+ return inside;
8460
+ return { from: Math.max(inside.from, Math.min(left, right)), to: Math.min(inside.to, Math.max(left, right)) };
8607
8461
  }
8608
8462
  function rectanglesForRange(view, className, range) {
8609
8463
  if (range.to <= view.viewport.from || range.from >= view.viewport.to)
@@ -8619,10 +8473,10 @@ function rectanglesForRange(view, className, range) {
8619
8473
  let visualStart = startBlock.type == BlockType.Text ? startBlock : null;
8620
8474
  let visualEnd = endBlock.type == BlockType.Text ? endBlock : null;
8621
8475
  if (visualStart && (view.lineWrapping || startBlock.widgetLineBreaks))
8622
- visualStart = wrappedLine(view, from, visualStart);
8476
+ visualStart = wrappedLine(view, from, 1, visualStart);
8623
8477
  if (visualEnd && (view.lineWrapping || endBlock.widgetLineBreaks))
8624
- visualEnd = wrappedLine(view, to, visualEnd);
8625
- if (visualStart && visualEnd && visualStart.from == visualEnd.from) {
8478
+ visualEnd = wrappedLine(view, to, -1, visualEnd);
8479
+ if (visualStart && visualEnd && visualStart.from == visualEnd.from && visualStart.to == visualEnd.to) {
8626
8480
  return pieces(drawForLine(range.from, range.to, visualStart));
8627
8481
  }
8628
8482
  else {
@@ -8877,14 +8731,19 @@ const selectionLayer = /*@__PURE__*/layer({
8877
8731
  });
8878
8732
  const themeSpec = {
8879
8733
  ".cm-line": {
8880
- "& ::selection": { backgroundColor: "transparent !important" },
8881
- "&::selection": { backgroundColor: "transparent !important" }
8734
+ "& ::selection, &::selection": { backgroundColor: "transparent !important" },
8735
+ },
8736
+ ".cm-content": {
8737
+ "& :focus": {
8738
+ caretColor: "initial !important",
8739
+ "&::selection, & ::selection": {
8740
+ backgroundColor: "Highlight !important"
8741
+ }
8742
+ }
8882
8743
  }
8883
8744
  };
8884
- if (CanHidePrimary) {
8885
- themeSpec[".cm-line"].caretColor = "transparent !important";
8886
- themeSpec[".cm-content"] = { caretColor: "transparent !important" };
8887
- }
8745
+ if (CanHidePrimary)
8746
+ themeSpec[".cm-line"].caretColor = themeSpec[".cm-content"].caretColor = "transparent !important";
8888
8747
  const hideNativeSelection = /*@__PURE__*/Prec.highest(/*@__PURE__*/EditorView.theme(themeSpec));
8889
8748
 
8890
8749
  const setDropCursorPos = /*@__PURE__*/StateEffect.define({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/view",
3
- "version": "6.26.4-edit-context.1",
3
+ "version": "6.26.4",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",