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

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/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
@@ -3744,9 +3738,16 @@ class InputState {
3744
3738
  // (after which we retroactively handle them and reset the DOM) to
3745
3739
  // avoid messing up the virtual keyboard state.
3746
3740
  this.pendingIOSKey = undefined;
3741
+ /**
3742
+ When enabled (>-1), tab presses are not given to key handlers,
3743
+ leaving the browser's default behavior. If >0, the mode expires
3744
+ at that timestamp, and any other keypress clears it.
3745
+ Esc enables temporary tab focus mode for two seconds when not
3746
+ otherwise handled.
3747
+ */
3748
+ this.tabFocusMode = -1;
3747
3749
  this.lastSelectionOrigin = null;
3748
3750
  this.lastSelectionTime = 0;
3749
- this.lastEscPress = 0;
3750
3751
  this.lastContextMenu = 0;
3751
3752
  this.scrollHandlers = [];
3752
3753
  this.handlers = Object.create(null);
@@ -3826,10 +3827,10 @@ class InputState {
3826
3827
  // Must always run, even if a custom handler handled the event
3827
3828
  this.lastKeyCode = event.keyCode;
3828
3829
  this.lastKeyTime = Date.now();
3829
- if (event.keyCode == 9 && Date.now() < this.lastEscPress + 2000)
3830
+ if (event.keyCode == 9 && this.tabFocusMode > -1 && (!this.tabFocusMode || Date.now() <= this.tabFocusMode))
3830
3831
  return true;
3831
- if (event.keyCode != 27 && modifierCodes.indexOf(event.keyCode) < 0)
3832
- this.view.inputState.lastEscPress = 0;
3832
+ if (this.tabFocusMode > 0 && event.keyCode != 27 && modifierCodes.indexOf(event.keyCode) < 0)
3833
+ this.tabFocusMode = -1;
3833
3834
  // Chrome for Android usually doesn't fire proper key events, but
3834
3835
  // occasionally does, usually surrounded by a bunch of complicated
3835
3836
  // composition changes. When an enter or backspace key event is
@@ -3890,7 +3891,6 @@ class InputState {
3890
3891
  this.mouseSelection = mouseSelection;
3891
3892
  }
3892
3893
  update(update) {
3893
- this.view.observer.update(update);
3894
3894
  if (this.mouseSelection)
3895
3895
  this.mouseSelection.update(update);
3896
3896
  if (this.draggedContent && update.docChanged)
@@ -4069,7 +4069,9 @@ class MouseSelection {
4069
4069
  this.mustSelect = false;
4070
4070
  }
4071
4071
  update(update) {
4072
- if (this.style.update(update))
4072
+ if (update.transactions.some(tr => tr.isUserEvent("input.type")))
4073
+ this.destroy();
4074
+ else if (this.style.update(update))
4073
4075
  setTimeout(() => this.select(this.lastEvent), 20);
4074
4076
  }
4075
4077
  }
@@ -4166,8 +4168,8 @@ observers.scroll = view => {
4166
4168
  };
4167
4169
  handlers.keydown = (view, event) => {
4168
4170
  view.inputState.setSelectionOrigin("select");
4169
- if (event.keyCode == 27)
4170
- view.inputState.lastEscPress = Date.now();
4171
+ if (event.keyCode == 27 && view.inputState.tabFocusMode != 0)
4172
+ view.inputState.tabFocusMode = Date.now() + 2000;
4171
4173
  return false;
4172
4174
  };
4173
4175
  observers.touchstart = (view, e) => {
@@ -4193,7 +4195,12 @@ handlers.mousedown = (view, event) => {
4193
4195
  let mustFocus = !view.hasFocus;
4194
4196
  view.inputState.startMouseSelection(new MouseSelection(view, event, style, mustFocus));
4195
4197
  if (mustFocus)
4196
- view.observer.ignore(() => focusPreventScroll(view.contentDOM));
4198
+ view.observer.ignore(() => {
4199
+ focusPreventScroll(view.contentDOM);
4200
+ let active = view.root.activeElement;
4201
+ if (active && !active.contains(view.contentDOM))
4202
+ active.blur();
4203
+ });
4197
4204
  let mouseSel = view.inputState.mouseSelection;
4198
4205
  if (mouseSel) {
4199
4206
  mouseSel.start(event);
@@ -4481,8 +4488,6 @@ observers.blur = view => {
4481
4488
  updateForFocusChange(view);
4482
4489
  };
4483
4490
  observers.compositionstart = observers.compositionupdate = view => {
4484
- if (view.observer.editContext)
4485
- return; // Composition handled by edit context
4486
4491
  if (view.inputState.compositionFirstChange == null)
4487
4492
  view.inputState.compositionFirstChange = true;
4488
4493
  if (view.inputState.composing < 0) {
@@ -4491,8 +4496,6 @@ observers.compositionstart = observers.compositionupdate = view => {
4491
4496
  }
4492
4497
  };
4493
4498
  observers.compositionend = view => {
4494
- if (view.observer.editContext)
4495
- return; // Composition handled by edit context
4496
4499
  view.inputState.composing = -1;
4497
4500
  view.inputState.compositionEndedAt = Date.now();
4498
4501
  view.inputState.compositionPendingKey = true;
@@ -5411,9 +5414,12 @@ class ViewState {
5411
5414
  this.heightOracle = new HeightOracle(guessWrapping);
5412
5415
  this.stateDeco = state.facet(decorations).filter(d => typeof d != "function");
5413
5416
  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);
5417
+ for (let i = 0; i < 2; i++) {
5418
+ this.viewport = this.getViewport(0, null);
5419
+ if (!this.updateForViewport())
5420
+ break;
5421
+ }
5415
5422
  this.updateViewportLines();
5416
- this.updateForViewport();
5417
5423
  this.lineGaps = this.ensureLineGaps([]);
5418
5424
  this.lineGapDeco = Decoration.set(this.lineGaps.map(gap => gap.draw(this, false)));
5419
5425
  this.computeVisibleRanges();
@@ -5428,13 +5434,18 @@ class ViewState {
5428
5434
  }
5429
5435
  }
5430
5436
  this.viewports = viewports.sort((a, b) => a.from - b.from);
5437
+ return this.updateScaler();
5438
+ }
5439
+ updateScaler() {
5440
+ let scaler = this.scaler;
5431
5441
  this.scaler = this.heightMap.height <= 7000000 /* VP.MaxDOMHeight */ ? IdScaler :
5432
5442
  new BigScaler(this.heightOracle, this.heightMap, this.viewports);
5443
+ return scaler.eq(this.scaler) ? 0 : 2 /* UpdateFlag.Height */;
5433
5444
  }
5434
5445
  updateViewportLines() {
5435
5446
  this.viewportLines = [];
5436
5447
  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));
5448
+ this.viewportLines.push(scaleBlock(block, this.scaler));
5438
5449
  });
5439
5450
  }
5440
5451
  update(update, scrollTarget = null) {
@@ -5460,11 +5471,10 @@ class ViewState {
5460
5471
  if (scrollTarget && (scrollTarget.range.head < viewport.from || scrollTarget.range.head > viewport.to) ||
5461
5472
  !this.viewportIsAppropriate(viewport))
5462
5473
  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;
5474
+ let viewportChange = viewport.from != this.viewport.from || viewport.to != this.viewport.to;
5465
5475
  this.viewport = viewport;
5466
- this.updateForViewport();
5467
- if (updateLines)
5476
+ update.flags |= this.updateForViewport();
5477
+ if (viewportChange || !update.changes.empty || (update.flags & 2 /* UpdateFlag.Height */))
5468
5478
  this.updateViewportLines();
5469
5479
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > (2000 /* LG.Margin */ << 1))
5470
5480
  this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps, update.changes)));
@@ -5562,9 +5572,12 @@ class ViewState {
5562
5572
  let viewportChange = !this.viewportIsAppropriate(this.viewport, bias) ||
5563
5573
  this.scrollTarget && (this.scrollTarget.range.head < this.viewport.from ||
5564
5574
  this.scrollTarget.range.head > this.viewport.to);
5565
- if (viewportChange)
5575
+ if (viewportChange) {
5576
+ if (result & 2 /* UpdateFlag.Height */)
5577
+ result |= this.updateScaler();
5566
5578
  this.viewport = this.getViewport(bias, this.scrollTarget);
5567
- this.updateForViewport();
5579
+ result |= this.updateForViewport();
5580
+ }
5568
5581
  if ((result & 2 /* UpdateFlag.Height */) || viewportChange)
5569
5582
  this.updateViewportLines();
5570
5583
  if (this.lineGaps.length || this.viewport.to - this.viewport.from > (2000 /* LG.Margin */ << 1))
@@ -5753,11 +5766,14 @@ class ViewState {
5753
5766
  return changed ? 4 /* UpdateFlag.Viewport */ : 0;
5754
5767
  }
5755
5768
  lineBlockAt(pos) {
5756
- return (pos >= this.viewport.from && pos <= this.viewport.to && this.viewportLines.find(b => b.from <= pos && b.to >= pos)) ||
5769
+ return (pos >= this.viewport.from && pos <= this.viewport.to &&
5770
+ this.viewportLines.find(b => b.from <= pos && b.to >= pos)) ||
5757
5771
  scaleBlock(this.heightMap.lineAt(pos, QueryType.ByPos, this.heightOracle, 0, 0), this.scaler);
5758
5772
  }
5759
5773
  lineBlockAtHeight(height) {
5760
- return scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
5774
+ return (height >= this.viewportLines[0].top && height <= this.viewportLines[this.viewportLines.length - 1].bottom &&
5775
+ this.viewportLines.find(l => l.top <= height && l.bottom >= height)) ||
5776
+ scaleBlock(this.heightMap.lineAt(this.scaler.fromDOM(height), QueryType.ByHeight, this.heightOracle, 0, 0), this.scaler);
5761
5777
  }
5762
5778
  scrollAnchorAt(scrollTop) {
5763
5779
  let block = this.lineBlockAtHeight(scrollTop + 8);
@@ -5832,7 +5848,8 @@ function find(array, f) {
5832
5848
  const IdScaler = {
5833
5849
  toDOM(n) { return n; },
5834
5850
  fromDOM(n) { return n; },
5835
- scale: 1
5851
+ scale: 1,
5852
+ eq(other) { return other == this; }
5836
5853
  };
5837
5854
  // When the height is too big (> VP.MaxDOMHeight), scale down the
5838
5855
  // regions outside the viewports so that the total height is
@@ -5875,6 +5892,12 @@ class BigScaler {
5875
5892
  domBase = vp.domBottom;
5876
5893
  }
5877
5894
  }
5895
+ eq(other) {
5896
+ if (!(other instanceof BigScaler))
5897
+ return false;
5898
+ return this.scale == other.scale && this.viewports.length == other.viewports.length &&
5899
+ this.viewports.every((vp, i) => vp.from == other.viewports[i].from && vp.to == other.viewports[i].to);
5900
+ }
5878
5901
  }
5879
5902
  function scaleBlock(block, scaler) {
5880
5903
  if (scaler.scale == 1)
@@ -6266,6 +6289,7 @@ class DOMChange {
6266
6289
  this.typeOver = typeOver;
6267
6290
  this.bounds = null;
6268
6291
  this.text = "";
6292
+ this.domChanged = start > -1;
6269
6293
  let { impreciseHead: iHead, impreciseAnchor: iAnchor } = view.docView;
6270
6294
  if (view.state.readOnly && start > -1) {
6271
6295
  // Ignore changes when the editor is read-only
@@ -6368,7 +6392,35 @@ function applyDOMChange(view, domChange) {
6368
6392
  change = { from: sel.from, to: sel.to, insert: Text.of([" "]) };
6369
6393
  }
6370
6394
  if (change) {
6371
- return applyDOMChangeInner(view, change, newSel, lastKey);
6395
+ if (browser.ios && view.inputState.flushIOSKey(change))
6396
+ return true;
6397
+ // Android browsers don't fire reasonable key events for enter,
6398
+ // backspace, or delete. So this detects changes that look like
6399
+ // they're caused by those keys, and reinterprets them as key
6400
+ // events. (Some of these keys are also handled by beforeinput
6401
+ // events and the pendingAndroidKey mechanism, but that's not
6402
+ // reliable in all situations.)
6403
+ if (browser.android &&
6404
+ ((change.to == sel.to &&
6405
+ // GBoard will sometimes remove a space it just inserted
6406
+ // after a completion when you press enter
6407
+ (change.from == sel.from || change.from == sel.from - 1 && view.state.sliceDoc(change.from, sel.from) == " ") &&
6408
+ change.insert.length == 1 && change.insert.lines == 2 &&
6409
+ dispatchKey(view.contentDOM, "Enter", 13)) ||
6410
+ ((change.from == sel.from - 1 && change.to == sel.to && change.insert.length == 0 ||
6411
+ lastKey == 8 && change.insert.length < change.to - change.from && change.to > sel.head) &&
6412
+ dispatchKey(view.contentDOM, "Backspace", 8)) ||
6413
+ (change.from == sel.from && change.to == sel.to + 1 && change.insert.length == 0 &&
6414
+ dispatchKey(view.contentDOM, "Delete", 46))))
6415
+ return true;
6416
+ let text = change.insert.toString();
6417
+ if (view.inputState.composing >= 0)
6418
+ view.inputState.composing++;
6419
+ let defaultTr;
6420
+ let defaultInsert = () => defaultTr || (defaultTr = applyDefaultInsert(view, change, newSel));
6421
+ if (!view.state.facet(inputHandler).some(h => h(view, change.from, change.to, text, defaultInsert)))
6422
+ view.dispatch(defaultInsert());
6423
+ return true;
6372
6424
  }
6373
6425
  else if (newSel && !newSel.main.eq(sel)) {
6374
6426
  let scrollIntoView = false, userEvent = "select";
@@ -6384,38 +6436,6 @@ function applyDOMChange(view, domChange) {
6384
6436
  return false;
6385
6437
  }
6386
6438
  }
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
6439
  function applyDefaultInsert(view, change, newSel) {
6420
6440
  let tr, startState = view.state, sel = startState.selection.main;
6421
6441
  if (change.from >= sel.from && change.to <= sel.to && change.to - change.from >= (sel.to - sel.from) / 3 &&
@@ -6542,7 +6562,6 @@ class DOMObserver {
6542
6562
  constructor(view) {
6543
6563
  this.view = view;
6544
6564
  this.active = false;
6545
- this.editContext = null;
6546
6565
  // The known selection. Kept in our own object, as opposed to just
6547
6566
  // directly accessing the selection because:
6548
6567
  // - Safari doesn't report the right selection in shadow DOM
@@ -6587,10 +6606,6 @@ class DOMObserver {
6587
6606
  else
6588
6607
  this.flush();
6589
6608
  });
6590
- if (window.EditContext && view.constructor.EDIT_CONTEXT !== false) {
6591
- this.editContext = new EditContextManager(view);
6592
- view.contentDOM.editContext = this.editContext.editContext;
6593
- }
6594
6609
  if (useCharData)
6595
6610
  this.onCharData = (event) => {
6596
6611
  this.queue.push({ target: event.target,
@@ -6641,8 +6656,6 @@ class DOMObserver {
6641
6656
  onScroll(e) {
6642
6657
  if (this.intersecting)
6643
6658
  this.flush(false);
6644
- if (this.editContext)
6645
- this.view.requestMeasure(this.editContext.measureReq);
6646
6659
  this.onScrollChanged(e);
6647
6660
  }
6648
6661
  onResize() {
@@ -6901,8 +6914,9 @@ class DOMObserver {
6901
6914
  }
6902
6915
  let startState = this.view.state;
6903
6916
  let handled = applyDOMChange(this.view, domChange);
6904
- // The view wasn't updated
6905
- if (this.view.state == startState)
6917
+ // The view wasn't updated but DOM/selection changes were seen. Reset the view.
6918
+ if (this.view.state == startState &&
6919
+ (domChange.domChanged || domChange.newSel && !domChange.newSel.main.eq(this.view.state.selection.main)))
6906
6920
  this.view.update([]);
6907
6921
  return handled;
6908
6922
  }
@@ -6951,10 +6965,6 @@ class DOMObserver {
6951
6965
  win.removeEventListener("beforeprint", this.onPrint);
6952
6966
  win.document.removeEventListener("selectionchange", this.onSelectionChange);
6953
6967
  }
6954
- update(update) {
6955
- if (this.editContext)
6956
- this.editContext.update(update);
6957
- }
6958
6968
  destroy() {
6959
6969
  var _a, _b, _c;
6960
6970
  this.stop();
@@ -7014,161 +7024,6 @@ function safariSelectionRangeHack(view, selection) {
7014
7024
  view.contentDOM.removeEventListener("beforeinput", read, true);
7015
7025
  return found ? buildSelectionRangeFromRange(view, found) : null;
7016
7026
  }
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
7027
 
7173
7028
  // The editor's update state machine looks something like this:
7174
7029
  //
@@ -8028,6 +7883,25 @@ class EditorView {
8028
7883
  return scrollIntoView.of(new ScrollTarget(EditorSelection.cursor(ref.from), "start", "start", ref.top - scrollTop, scrollLeft, true));
8029
7884
  }
8030
7885
  /**
7886
+ Enable or disable tab-focus mode, which disables key bindings
7887
+ for Tab and Shift-Tab, letting the browser's default
7888
+ focus-changing behavior go through instead. This is useful to
7889
+ prevent trapping keyboard users in your editor.
7890
+
7891
+ Without argument, this toggles the mode. With a boolean, it
7892
+ enables (true) or disables it (false). Given a number, it
7893
+ temporarily enables the mode until that number of milliseconds
7894
+ have passed or another non-Tab key is pressed.
7895
+ */
7896
+ setTabFocusMode(to) {
7897
+ if (to == null)
7898
+ this.inputState.tabFocusMode = this.inputState.tabFocusMode < 0 ? 0 : -1;
7899
+ else if (typeof to == "boolean")
7900
+ this.inputState.tabFocusMode = to ? 0 : -1;
7901
+ else if (this.inputState.tabFocusMode != 0)
7902
+ this.inputState.tabFocusMode = Date.now() + to;
7903
+ }
7904
+ /**
8031
7905
  Returns an extension that can be used to add DOM event handlers.
8032
7906
  The value should be an object mapping event names to handler
8033
7907
  functions. For any given event, such functions are ordered by
@@ -8599,11 +8473,17 @@ function getBase(view) {
8599
8473
  let left = view.textDirection == Direction.LTR ? rect.left : rect.right - view.scrollDOM.clientWidth * view.scaleX;
8600
8474
  return { left: left - view.scrollDOM.scrollLeft * view.scaleX, top: rect.top - view.scrollDOM.scrollTop * view.scaleY };
8601
8475
  }
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 };
8476
+ function wrappedLine(view, pos, side, inside) {
8477
+ let coords = view.coordsAtPos(pos, side * 2);
8478
+ if (!coords)
8479
+ return inside;
8480
+ let editorRect = view.dom.getBoundingClientRect();
8481
+ let y = (coords.top + coords.bottom) / 2;
8482
+ let left = view.posAtCoords({ x: editorRect.left + 1, y });
8483
+ let right = view.posAtCoords({ x: editorRect.right - 1, y });
8484
+ if (left == null || right == null)
8485
+ return inside;
8486
+ return { from: Math.max(inside.from, Math.min(left, right)), to: Math.min(inside.to, Math.max(left, right)) };
8607
8487
  }
8608
8488
  function rectanglesForRange(view, className, range) {
8609
8489
  if (range.to <= view.viewport.from || range.from >= view.viewport.to)
@@ -8619,10 +8499,10 @@ function rectanglesForRange(view, className, range) {
8619
8499
  let visualStart = startBlock.type == BlockType.Text ? startBlock : null;
8620
8500
  let visualEnd = endBlock.type == BlockType.Text ? endBlock : null;
8621
8501
  if (visualStart && (view.lineWrapping || startBlock.widgetLineBreaks))
8622
- visualStart = wrappedLine(view, from, visualStart);
8502
+ visualStart = wrappedLine(view, from, 1, visualStart);
8623
8503
  if (visualEnd && (view.lineWrapping || endBlock.widgetLineBreaks))
8624
- visualEnd = wrappedLine(view, to, visualEnd);
8625
- if (visualStart && visualEnd && visualStart.from == visualEnd.from) {
8504
+ visualEnd = wrappedLine(view, to, -1, visualEnd);
8505
+ if (visualStart && visualEnd && visualStart.from == visualEnd.from && visualStart.to == visualEnd.to) {
8626
8506
  return pieces(drawForLine(range.from, range.to, visualStart));
8627
8507
  }
8628
8508
  else {
@@ -8877,14 +8757,19 @@ const selectionLayer = /*@__PURE__*/layer({
8877
8757
  });
8878
8758
  const themeSpec = {
8879
8759
  ".cm-line": {
8880
- "& ::selection": { backgroundColor: "transparent !important" },
8881
- "&::selection": { backgroundColor: "transparent !important" }
8760
+ "& ::selection, &::selection": { backgroundColor: "transparent !important" },
8761
+ },
8762
+ ".cm-content": {
8763
+ "& :focus": {
8764
+ caretColor: "initial !important",
8765
+ "&::selection, & ::selection": {
8766
+ backgroundColor: "Highlight !important"
8767
+ }
8768
+ }
8882
8769
  }
8883
8770
  };
8884
- if (CanHidePrimary) {
8885
- themeSpec[".cm-line"].caretColor = "transparent !important";
8886
- themeSpec[".cm-content"] = { caretColor: "transparent !important" };
8887
- }
8771
+ if (CanHidePrimary)
8772
+ themeSpec[".cm-line"].caretColor = themeSpec[".cm-content"].caretColor = "transparent !important";
8888
8773
  const hideNativeSelection = /*@__PURE__*/Prec.highest(/*@__PURE__*/EditorView.theme(themeSpec));
8889
8774
 
8890
8775
  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.27.0",
4
4
  "description": "DOM view component for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",