@codemirror/autocomplete 6.9.0 → 6.9.2

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,17 @@
1
+ ## 6.9.2 (2023-10-06)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a bug in `completeAnyWord` that could cause it to generate invalid regular expressions and crash.
6
+
7
+ ## 6.9.1 (2023-09-14)
8
+
9
+ ### Bug fixes
10
+
11
+ Make sure the cursor is scrolled into view after inserting completion text.
12
+
13
+ Make sure scrolling completions into view doesn't get confused when the tooltip is scaled.
14
+
1
15
  ## 6.9.0 (2023-07-18)
2
16
 
3
17
  ### New features
package/dist/index.cjs CHANGED
@@ -1,7 +1,5 @@
1
1
  'use strict';
2
2
 
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
3
  var state = require('@codemirror/state');
6
4
  var view = require('@codemirror/view');
7
5
  var language = require('@codemirror/language');
@@ -175,7 +173,7 @@ function insertCompletionText(state$1, text, from, to) {
175
173
  changes: { from: range.from + fromOff, to: to == main.from ? range.to : range.from + toOff, insert: text },
176
174
  range: state.EditorSelection.cursor(range.from + fromOff + text.length)
177
175
  };
178
- })), { userEvent: "input.complete" });
176
+ })), { scrollIntoView: true, userEvent: "input.complete" });
179
177
  }
180
178
  const SourceCache = new WeakMap();
181
179
  function asSource(source) {
@@ -227,7 +225,7 @@ class FuzzyMatcher {
227
225
  // is. See `Penalty` above.
228
226
  match(word) {
229
227
  if (this.pattern.length == 0)
230
- return this.ret(-100 /* NotFull */, []);
228
+ return this.ret(-100 /* Penalty.NotFull */, []);
231
229
  if (word.length < this.pattern.length)
232
230
  return false;
233
231
  let { chars, folded, any, precise, byWord } = this;
@@ -235,17 +233,17 @@ class FuzzyMatcher {
235
233
  // at the start
236
234
  if (chars.length == 1) {
237
235
  let first = state.codePointAt(word, 0), firstSize = state.codePointSize(first);
238
- let score = firstSize == word.length ? 0 : -100 /* NotFull */;
236
+ let score = firstSize == word.length ? 0 : -100 /* Penalty.NotFull */;
239
237
  if (first == chars[0]) ;
240
238
  else if (first == folded[0])
241
- score += -200 /* CaseFold */;
239
+ score += -200 /* Penalty.CaseFold */;
242
240
  else
243
241
  return false;
244
242
  return this.ret(score, [0, firstSize]);
245
243
  }
246
244
  let direct = word.indexOf(this.pattern);
247
245
  if (direct == 0)
248
- return this.ret(word.length == this.pattern.length ? 0 : -100 /* NotFull */, [0, this.pattern.length]);
246
+ return this.ret(word.length == this.pattern.length ? 0 : -100 /* Penalty.NotFull */, [0, this.pattern.length]);
249
247
  let len = chars.length, anyTo = 0;
250
248
  if (direct < 0) {
251
249
  for (let i = 0, e = Math.min(word.length, 200); i < e && anyTo < len;) {
@@ -269,7 +267,7 @@ class FuzzyMatcher {
269
267
  let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
270
268
  let hasLower = /[a-z]/.test(word), wordAdjacent = true;
271
269
  // Go over the option's text, scanning for the various kinds of matches
272
- for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* NonWord */; i < e && byWordTo < len;) {
270
+ for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* Tp.NonWord */; i < e && byWordTo < len;) {
273
271
  let next = state.codePointAt(word, i);
274
272
  if (direct < 0) {
275
273
  if (preciseTo < len && next == chars[preciseTo])
@@ -287,9 +285,9 @@ class FuzzyMatcher {
287
285
  }
288
286
  }
289
287
  let ch, type = next < 0xff
290
- ? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Lower */ : next >= 65 && next <= 90 ? 1 /* Upper */ : 0 /* NonWord */)
291
- : ((ch = state.fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Upper */ : ch != ch.toUpperCase() ? 2 /* Lower */ : 0 /* NonWord */);
292
- if (!i || type == 1 /* Upper */ && hasLower || prevType == 0 /* NonWord */ && type != 0 /* NonWord */) {
288
+ ? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Tp.Lower */ : next >= 65 && next <= 90 ? 1 /* Tp.Upper */ : 0 /* Tp.NonWord */)
289
+ : ((ch = state.fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Tp.Upper */ : ch != ch.toUpperCase() ? 2 /* Tp.Lower */ : 0 /* Tp.NonWord */);
290
+ if (!i || type == 1 /* Tp.Upper */ && hasLower || prevType == 0 /* Tp.NonWord */ && type != 0 /* Tp.NonWord */) {
293
291
  if (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
294
292
  byWord[byWordTo++] = i;
295
293
  else if (byWord.length)
@@ -299,18 +297,18 @@ class FuzzyMatcher {
299
297
  i += state.codePointSize(next);
300
298
  }
301
299
  if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
302
- return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0), byWord, word);
300
+ return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0), byWord, word);
303
301
  if (adjacentTo == len && adjacentStart == 0)
304
- return this.ret(-200 /* CaseFold */ - word.length + (adjacentEnd == word.length ? 0 : -100 /* NotFull */), [0, adjacentEnd]);
302
+ return this.ret(-200 /* Penalty.CaseFold */ - word.length + (adjacentEnd == word.length ? 0 : -100 /* Penalty.NotFull */), [0, adjacentEnd]);
305
303
  if (direct > -1)
306
- return this.ret(-700 /* NotStart */ - word.length, [direct, direct + this.pattern.length]);
304
+ return this.ret(-700 /* Penalty.NotStart */ - word.length, [direct, direct + this.pattern.length]);
307
305
  if (adjacentTo == len)
308
- return this.ret(-200 /* CaseFold */ + -700 /* NotStart */ - word.length, [adjacentStart, adjacentEnd]);
306
+ return this.ret(-200 /* Penalty.CaseFold */ + -700 /* Penalty.NotStart */ - word.length, [adjacentStart, adjacentEnd]);
309
307
  if (byWordTo == len)
310
- return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */ +
311
- (wordAdjacent ? 0 : -1100 /* Gap */), byWord, word);
308
+ return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0) + -700 /* Penalty.NotStart */ +
309
+ (wordAdjacent ? 0 : -1100 /* Penalty.Gap */), byWord, word);
312
310
  return chars.length == 2 ? false
313
- : this.result((any[0] ? -700 /* NotStart */ : 0) + -200 /* CaseFold */ + -1100 /* Gap */, any, word);
311
+ : this.result((any[0] ? -700 /* Penalty.NotStart */ : 0) + -200 /* Penalty.CaseFold */ + -1100 /* Penalty.Gap */, any, word);
314
312
  }
315
313
  result(score, positions, word) {
316
314
  let result = [], i = 0;
@@ -357,7 +355,7 @@ const completionConfig = state.Facet.define({
357
355
  function joinClass(a, b) {
358
356
  return a ? b ? a + " " + b : a : b;
359
357
  }
360
- function defaultPositionInfo(view$1, list, option, info, space) {
358
+ function defaultPositionInfo(view$1, list, option, info, space, tooltip) {
361
359
  let rtl = view$1.textDirection == view.Direction.RTL, left = rtl, narrow = false;
362
360
  let side = "top", offset, maxWidth;
363
361
  let spaceLeft = list.left - space.left, spaceRight = space.right - list.right;
@@ -368,11 +366,11 @@ function defaultPositionInfo(view$1, list, option, info, space) {
368
366
  left = true;
369
367
  if (infoWidth <= (left ? spaceLeft : spaceRight)) {
370
368
  offset = Math.max(space.top, Math.min(option.top, space.bottom - infoHeight)) - list.top;
371
- maxWidth = Math.min(400 /* Width */, left ? spaceLeft : spaceRight);
369
+ maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight);
372
370
  }
373
371
  else {
374
372
  narrow = true;
375
- maxWidth = Math.min(400 /* Width */, (rtl ? list.right : space.right - list.left) - 30 /* Margin */);
373
+ maxWidth = Math.min(400 /* Info.Width */, (rtl ? list.right : space.right - list.left) - 30 /* Info.Margin */);
376
374
  let spaceBelow = space.bottom - list.bottom;
377
375
  if (spaceBelow >= infoHeight || spaceBelow > list.top) { // Below the completion
378
376
  offset = option.bottom - list.top;
@@ -382,8 +380,10 @@ function defaultPositionInfo(view$1, list, option, info, space) {
382
380
  offset = list.bottom - option.top;
383
381
  }
384
382
  }
383
+ let scaleY = (list.bottom - list.top) / tooltip.offsetHeight;
384
+ let scaleX = (list.right - list.left) / tooltip.offsetWidth;
385
385
  return {
386
- style: `${side}: ${offset}px; max-width: ${maxWidth}px`,
386
+ style: `${side}: ${offset / scaleY}px; max-width: ${maxWidth / scaleX}px`,
387
387
  class: "cm-completionInfo-" + (narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right")
388
388
  };
389
389
  }
@@ -485,22 +485,32 @@ class CompletionTooltip {
485
485
  e.relatedTarget != view.contentDOM)
486
486
  view.dispatch({ effects: closeCompletionEffect.of(null) });
487
487
  });
488
- this.list = this.dom.appendChild(this.createListBox(options, cState.id, this.range));
488
+ this.showOptions(options, cState.id);
489
+ }
490
+ mount() { this.updateSel(); }
491
+ showOptions(options, id) {
492
+ if (this.list)
493
+ this.list.remove();
494
+ this.list = this.dom.appendChild(this.createListBox(options, id, this.range));
489
495
  this.list.addEventListener("scroll", () => {
490
496
  if (this.info)
491
497
  this.view.requestMeasure(this.placeInfoReq);
492
498
  });
493
499
  }
494
- mount() { this.updateSel(); }
495
500
  update(update) {
496
- var _a, _b, _c;
501
+ var _a;
497
502
  let cState = update.state.field(this.stateField);
498
503
  let prevState = update.startState.field(this.stateField);
499
504
  this.updateTooltipClass(update.state);
500
505
  if (cState != prevState) {
506
+ let { options, selected, disabled } = cState.open;
507
+ if (!prevState.open || prevState.open.options != options) {
508
+ this.range = rangeAroundSelected(options.length, selected, update.state.facet(completionConfig).maxRenderedOptions);
509
+ this.showOptions(options, cState.id);
510
+ }
501
511
  this.updateSel();
502
- if (((_a = cState.open) === null || _a === void 0 ? void 0 : _a.disabled) != ((_b = prevState.open) === null || _b === void 0 ? void 0 : _b.disabled))
503
- this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!((_c = cState.open) === null || _c === void 0 ? void 0 : _c.disabled));
512
+ if (disabled != ((_a = prevState.open) === null || _a === void 0 ? void 0 : _a.disabled))
513
+ this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!disabled);
504
514
  }
505
515
  }
506
516
  updateTooltipClass(state) {
@@ -524,12 +534,7 @@ class CompletionTooltip {
524
534
  let cState = this.view.state.field(this.stateField), open = cState.open;
525
535
  if (open.selected > -1 && open.selected < this.range.from || open.selected >= this.range.to) {
526
536
  this.range = rangeAroundSelected(open.options.length, open.selected, this.view.state.facet(completionConfig).maxRenderedOptions);
527
- this.list.remove();
528
- this.list = this.dom.appendChild(this.createListBox(open.options, cState.id, this.range));
529
- this.list.addEventListener("scroll", () => {
530
- if (this.info)
531
- this.view.requestMeasure(this.placeInfoReq);
532
- });
537
+ this.showOptions(open.options, cState.id);
533
538
  }
534
539
  if (this.updateSelectedOption(open.selected)) {
535
540
  this.destroyInfo();
@@ -603,7 +608,7 @@ class CompletionTooltip {
603
608
  if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 ||
604
609
  selRect.bottom < Math.max(space.top, listRect.top) + 10)
605
610
  return null;
606
- return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space);
611
+ return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space, this.dom);
607
612
  }
608
613
  placeInfo(pos) {
609
614
  if (this.info) {
@@ -669,18 +674,17 @@ class CompletionTooltip {
669
674
  this.destroyInfo();
670
675
  }
671
676
  }
672
- // We allocate a new function instance every time the completion
673
- // changes to force redrawing/repositioning of the tooltip
674
677
  function completionTooltip(stateField, applyCompletion) {
675
678
  return (view) => new CompletionTooltip(view, stateField, applyCompletion);
676
679
  }
677
680
  function scrollIntoView(container, element) {
678
681
  let parent = container.getBoundingClientRect();
679
682
  let self = element.getBoundingClientRect();
683
+ let scaleY = parent.height / container.offsetHeight;
680
684
  if (self.top < parent.top)
681
- container.scrollTop -= parent.top - self.top;
685
+ container.scrollTop -= (parent.top - self.top) / scaleY;
682
686
  else if (self.bottom > parent.bottom)
683
- container.scrollTop += self.bottom - parent.bottom;
687
+ container.scrollTop += (self.bottom - parent.bottom) / scaleY;
684
688
  }
685
689
 
686
690
  // Used to pick a preferred option when two options with the same
@@ -763,7 +767,7 @@ class CompletionDialog {
763
767
  static build(active, state, id, prev, conf) {
764
768
  let options = sortOptions(active, state);
765
769
  if (!options.length) {
766
- return prev && active.some(a => a.state == 1 /* Pending */) ?
770
+ return prev && active.some(a => a.state == 1 /* State.Pending */) ?
767
771
  new CompletionDialog(prev.options, prev.attrs, prev.tooltip, prev.timestamp, prev.selected, true) : null;
768
772
  }
769
773
  let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
@@ -777,7 +781,7 @@ class CompletionDialog {
777
781
  }
778
782
  return new CompletionDialog(options, makeAttrs(id, selected), {
779
783
  pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
780
- create: completionTooltip(completionState, applyCompletion),
784
+ create: createTooltip,
781
785
  above: conf.aboveCursor,
782
786
  }, prev ? prev.timestamp : Date.now(), selected, false);
783
787
  }
@@ -800,7 +804,7 @@ class CompletionState {
800
804
  state.languageDataAt("autocomplete", cur(state)).map(asSource);
801
805
  let active = sources.map(source => {
802
806
  let value = this.active.find(s => s.source == source) ||
803
- new ActiveSource(source, this.active.some(a => a.state != 0 /* Inactive */) ? 1 /* Pending */ : 0 /* Inactive */);
807
+ new ActiveSource(source, this.active.some(a => a.state != 0 /* State.Inactive */) ? 1 /* State.Pending */ : 0 /* State.Inactive */);
804
808
  return value.update(tr, conf);
805
809
  });
806
810
  if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
@@ -811,10 +815,10 @@ class CompletionState {
811
815
  if (tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
812
816
  !sameResults(active, this.active))
813
817
  open = CompletionDialog.build(active, state, this.id, open, conf);
814
- else if (open && open.disabled && !active.some(a => a.state == 1 /* Pending */))
818
+ else if (open && open.disabled && !active.some(a => a.state == 1 /* State.Pending */))
815
819
  open = null;
816
- if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
817
- active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
820
+ if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult()))
821
+ active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* State.Inactive */) : a);
818
822
  for (let effect of tr.effects)
819
823
  if (effect.is(setSelectedEffect))
820
824
  open = open && open.setSelected(effect.value, this.id);
@@ -868,13 +872,13 @@ class ActiveSource {
868
872
  value = value.handleUserEvent(tr, event, conf);
869
873
  else if (tr.docChanged)
870
874
  value = value.handleChange(tr);
871
- else if (tr.selection && value.state != 0 /* Inactive */)
872
- value = new ActiveSource(value.source, 0 /* Inactive */);
875
+ else if (tr.selection && value.state != 0 /* State.Inactive */)
876
+ value = new ActiveSource(value.source, 0 /* State.Inactive */);
873
877
  for (let effect of tr.effects) {
874
878
  if (effect.is(startCompletionEffect))
875
- value = new ActiveSource(value.source, 1 /* Pending */, effect.value ? cur(tr.state) : -1);
879
+ value = new ActiveSource(value.source, 1 /* State.Pending */, effect.value ? cur(tr.state) : -1);
876
880
  else if (effect.is(closeCompletionEffect))
877
- value = new ActiveSource(value.source, 0 /* Inactive */);
881
+ value = new ActiveSource(value.source, 0 /* State.Inactive */);
878
882
  else if (effect.is(setActiveEffect))
879
883
  for (let active of effect.value)
880
884
  if (active.source == value.source)
@@ -883,10 +887,10 @@ class ActiveSource {
883
887
  return value;
884
888
  }
885
889
  handleUserEvent(tr, type, conf) {
886
- return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* Pending */);
890
+ return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* State.Pending */);
887
891
  }
888
892
  handleChange(tr) {
889
- return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
893
+ return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
890
894
  }
891
895
  map(changes) {
892
896
  return changes.empty || this.explicitPos < 0 ? this : new ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos));
@@ -894,7 +898,7 @@ class ActiveSource {
894
898
  }
895
899
  class ActiveResult extends ActiveSource {
896
900
  constructor(source, explicitPos, result, from, to) {
897
- super(source, 2 /* Result */, explicitPos);
901
+ super(source, 2 /* State.Result */, explicitPos);
898
902
  this.result = result;
899
903
  this.from = from;
900
904
  this.to = to;
@@ -907,17 +911,17 @@ class ActiveResult extends ActiveSource {
907
911
  if ((this.explicitPos < 0 ? pos <= from : pos < this.from) ||
908
912
  pos > to ||
909
913
  type == "delete" && cur(tr.startState) == this.from)
910
- return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */);
914
+ return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* State.Pending */ : 0 /* State.Inactive */);
911
915
  let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos), updated;
912
916
  if (checkValid(this.result.validFor, tr.state, from, to))
913
917
  return new ActiveResult(this.source, explicitPos, this.result, from, to);
914
918
  if (this.result.update &&
915
919
  (updated = this.result.update(this.result, from, to, new CompletionContext(tr.state, pos, explicitPos >= 0))))
916
920
  return new ActiveResult(this.source, explicitPos, updated, updated.from, (_a = updated.to) !== null && _a !== void 0 ? _a : cur(tr.state));
917
- return new ActiveSource(this.source, 1 /* Pending */, explicitPos);
921
+ return new ActiveSource(this.source, 1 /* State.Pending */, explicitPos);
918
922
  }
919
923
  handleChange(tr) {
920
- return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
924
+ return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
921
925
  }
922
926
  map(mapping) {
923
927
  return mapping.empty ? this :
@@ -953,6 +957,7 @@ function applyCompletion(view, option) {
953
957
  apply(view, option.completion, result.from, result.to);
954
958
  return true;
955
959
  }
960
+ const createTooltip = completionTooltip(completionState, applyCompletion);
956
961
 
957
962
  /**
958
963
  Returns a command that moves the completion selection forward or
@@ -1003,7 +1008,7 @@ Close the currently active completion.
1003
1008
  */
1004
1009
  const closeCompletion = (view) => {
1005
1010
  let cState = view.state.field(completionState, false);
1006
- if (!cState || !cState.active.some(a => a.state != 0 /* Inactive */))
1011
+ if (!cState || !cState.active.some(a => a.state != 0 /* State.Inactive */))
1007
1012
  return false;
1008
1013
  view.dispatch({ effects: closeCompletionEffect.of(null) });
1009
1014
  return true;
@@ -1026,9 +1031,9 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
1026
1031
  this.debounceUpdate = -1;
1027
1032
  this.running = [];
1028
1033
  this.debounceAccept = -1;
1029
- this.composing = 0 /* None */;
1034
+ this.composing = 0 /* CompositionState.None */;
1030
1035
  for (let active of view.state.field(completionState).active)
1031
- if (active.state == 1 /* Pending */)
1036
+ if (active.state == 1 /* State.Pending */)
1032
1037
  this.startQuery(active);
1033
1038
  }
1034
1039
  update(update) {
@@ -1059,21 +1064,21 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
1059
1064
  }
1060
1065
  if (this.debounceUpdate > -1)
1061
1066
  clearTimeout(this.debounceUpdate);
1062
- this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.active.source == a.source))
1067
+ this.debounceUpdate = cState.active.some(a => a.state == 1 /* State.Pending */ && !this.running.some(q => q.active.source == a.source))
1063
1068
  ? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
1064
- if (this.composing != 0 /* None */)
1069
+ if (this.composing != 0 /* CompositionState.None */)
1065
1070
  for (let tr of update.transactions) {
1066
1071
  if (getUserEvent(tr) == "input")
1067
- this.composing = 2 /* Changed */;
1068
- else if (this.composing == 2 /* Changed */ && tr.selection)
1069
- this.composing = 3 /* ChangedAndMoved */;
1072
+ this.composing = 2 /* CompositionState.Changed */;
1073
+ else if (this.composing == 2 /* CompositionState.Changed */ && tr.selection)
1074
+ this.composing = 3 /* CompositionState.ChangedAndMoved */;
1070
1075
  }
1071
1076
  }
1072
1077
  startUpdate() {
1073
1078
  this.debounceUpdate = -1;
1074
1079
  let { state } = this.view, cState = state.field(completionState);
1075
1080
  for (let active of cState.active) {
1076
- if (active.state == 1 /* Pending */ && !this.running.some(r => r.active.source == active.source))
1081
+ if (active.state == 1 /* State.Pending */ && !this.running.some(r => r.active.source == active.source))
1077
1082
  this.startQuery(active);
1078
1083
  }
1079
1084
  }
@@ -1124,14 +1129,14 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
1124
1129
  }
1125
1130
  }
1126
1131
  let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
1127
- if (current && current.state == 1 /* Pending */) {
1132
+ if (current && current.state == 1 /* State.Pending */) {
1128
1133
  if (query.done == null) {
1129
1134
  // Explicitly failed. Should clear the pending status if it
1130
1135
  // hasn't been re-set in the meantime.
1131
- let active = new ActiveSource(query.active.source, 0 /* Inactive */);
1136
+ let active = new ActiveSource(query.active.source, 0 /* State.Inactive */);
1132
1137
  for (let tr of query.updates)
1133
1138
  active = active.update(tr, conf);
1134
- if (active.state != 1 /* Pending */)
1139
+ if (active.state != 1 /* State.Pending */)
1135
1140
  updated.push(active);
1136
1141
  }
1137
1142
  else {
@@ -1154,15 +1159,15 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
1154
1159
  }
1155
1160
  },
1156
1161
  compositionstart() {
1157
- this.composing = 1 /* Started */;
1162
+ this.composing = 1 /* CompositionState.Started */;
1158
1163
  },
1159
1164
  compositionend() {
1160
- if (this.composing == 3 /* ChangedAndMoved */) {
1165
+ if (this.composing == 3 /* CompositionState.ChangedAndMoved */) {
1161
1166
  // Safari fires compositionend events synchronously, possibly
1162
1167
  // from inside an update, so dispatch asynchronously to avoid reentrancy
1163
1168
  setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
1164
1169
  }
1165
- this.composing = 0 /* None */;
1170
+ this.composing = 0 /* CompositionState.None */;
1166
1171
  }
1167
1172
  }
1168
1173
  });
@@ -1222,13 +1227,13 @@ const baseTheme = view.EditorView.baseTheme({
1222
1227
  position: "absolute",
1223
1228
  padding: "3px 9px",
1224
1229
  width: "max-content",
1225
- maxWidth: `${400 /* Width */}px`,
1230
+ maxWidth: `${400 /* Info.Width */}px`,
1226
1231
  boxSizing: "border-box"
1227
1232
  },
1228
1233
  ".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
1229
1234
  ".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
1230
- ".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Margin */}px` },
1231
- ".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Margin */}px` },
1235
+ ".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Info.Margin */}px` },
1236
+ ".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Info.Margin */}px` },
1232
1237
  "&light .cm-snippetField": { backgroundColor: "#00000022" },
1233
1238
  "&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
1234
1239
  ".cm-snippetFieldPosition": {
@@ -1479,7 +1484,8 @@ function moveField(dir) {
1479
1484
  let next = active.active + dir, last = dir > 0 && !active.ranges.some(r => r.field == next + dir);
1480
1485
  dispatch(state.update({
1481
1486
  selection: fieldSelection(active.ranges, next),
1482
- effects: setActive.of(last ? null : new ActiveSnippet(active.ranges, next))
1487
+ effects: setActive.of(last ? null : new ActiveSnippet(active.ranges, next)),
1488
+ scrollIntoView: true
1483
1489
  }));
1484
1490
  return true;
1485
1491
  };
@@ -1551,14 +1557,16 @@ const snippetPointerHandler = view.EditorView.domEventHandlers({
1551
1557
  return false;
1552
1558
  view.dispatch({
1553
1559
  selection: fieldSelection(active.ranges, match.field),
1554
- effects: setActive.of(active.ranges.some(r => r.field > match.field) ? new ActiveSnippet(active.ranges, match.field) : null)
1560
+ effects: setActive.of(active.ranges.some(r => r.field > match.field)
1561
+ ? new ActiveSnippet(active.ranges, match.field) : null),
1562
+ scrollIntoView: true
1555
1563
  });
1556
1564
  return true;
1557
1565
  }
1558
1566
  });
1559
1567
 
1560
1568
  function wordRE(wordChars) {
1561
- let escaped = wordChars.replace(/[\\[.+*?(){|^$]/g, "\\$&");
1569
+ let escaped = wordChars.replace(/[\]\-\\]/g, "\\$&");
1562
1570
  try {
1563
1571
  return new RegExp(`[\\p{Alphabetic}\\p{Number}_${escaped}]+`, "ug");
1564
1572
  }
@@ -1581,7 +1589,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
1581
1589
  if (!seen[m[0]] && pos + m.index != ignoreAt) {
1582
1590
  result.push({ type: "text", label: m[0] });
1583
1591
  seen[m[0]] = true;
1584
- if (result.length >= 2000 /* MaxList */)
1592
+ if (result.length >= 2000 /* C.MaxList */)
1585
1593
  return;
1586
1594
  }
1587
1595
  }
@@ -1589,7 +1597,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
1589
1597
  }
1590
1598
  }
1591
1599
  function collectWords(doc, cache, wordRE, to, ignoreAt) {
1592
- let big = doc.length >= 1000 /* MinCacheLen */;
1600
+ let big = doc.length >= 1000 /* C.MinCacheLen */;
1593
1601
  let cached = big && cache.get(doc);
1594
1602
  if (cached)
1595
1603
  return cached;
@@ -1597,7 +1605,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
1597
1605
  if (doc.children) {
1598
1606
  let pos = 0;
1599
1607
  for (let ch of doc.children) {
1600
- if (ch.length >= 1000 /* MinCacheLen */) {
1608
+ if (ch.length >= 1000 /* C.MinCacheLen */) {
1601
1609
  for (let c of collectWords(ch, cache, wordRE, to - pos, ignoreAt - pos)) {
1602
1610
  if (!seen[c.label]) {
1603
1611
  seen[c.label] = true;
@@ -1614,7 +1622,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
1614
1622
  else {
1615
1623
  storeWords(doc, wordRE, result, seen, ignoreAt);
1616
1624
  }
1617
- if (big && result.length < 2000 /* MaxList */)
1625
+ if (big && result.length < 2000 /* C.MaxList */)
1618
1626
  cache.set(doc, result);
1619
1627
  return result;
1620
1628
  }
@@ -1630,7 +1638,7 @@ const completeAnyWord = context => {
1630
1638
  if (!token && !context.explicit)
1631
1639
  return null;
1632
1640
  let from = token ? token.from : context.pos;
1633
- let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* Range */, from);
1641
+ let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* C.Range */, from);
1634
1642
  return { from, options, validFor: mapRE(re, s => "^" + s) };
1635
1643
  };
1636
1644
 
@@ -1922,8 +1930,8 @@ returns `null`.
1922
1930
  */
1923
1931
  function completionStatus(state) {
1924
1932
  let cState = state.field(completionState, false);
1925
- return cState && cState.active.some(a => a.state == 1 /* Pending */) ? "pending"
1926
- : cState && cState.active.some(a => a.state != 0 /* Inactive */) ? "active" : null;
1933
+ return cState && cState.active.some(a => a.state == 1 /* State.Pending */) ? "pending"
1934
+ : cState && cState.active.some(a => a.state != 0 /* State.Inactive */) ? "active" : null;
1927
1935
  }
1928
1936
  const completionArrayCache = new WeakMap;
1929
1937
  /**
package/dist/index.d.cts CHANGED
@@ -9,7 +9,7 @@ Objects type used to represent individual completions.
9
9
  interface Completion {
10
10
  /**
11
11
  The label to show in the completion picker. This is what input
12
- is matched agains to determine whether a completion matches (and
12
+ is matched against to determine whether a completion matches (and
13
13
  how well it matches).
14
14
  */
15
15
  label: string;
@@ -75,7 +75,7 @@ The type returned from
75
75
  node, null to indicate there is no info, or an object with an
76
76
  optional `destroy` method that cleans up the node.
77
77
  */
78
- declare type CompletionInfo = Node | null | {
78
+ type CompletionInfo = Node | null | {
79
79
  dom: Node;
80
80
  destroy?(): void;
81
81
  };
@@ -196,7 +196,7 @@ may return its [result](https://codemirror.net/6/docs/ref/#autocomplete.Completi
196
196
  synchronously or as a promise. Returning null indicates no
197
197
  completions are available.
198
198
  */
199
- declare type CompletionSource = (context: CompletionContext) => CompletionResult | null | Promise<CompletionResult | null>;
199
+ type CompletionSource = (context: CompletionContext) => CompletionResult | null | Promise<CompletionResult | null>;
200
200
  /**
201
201
  Interface for objects returned by completion sources.
202
202
  */
@@ -344,13 +344,13 @@ interface CompletionConfig {
344
344
  position: number;
345
345
  }[];
346
346
  /**
347
- By default, [info](https://codemirror.net/6/docs/ref/#autocomplet.Completion.info) tooltips are
348
- placed to the side of the selected. This option can be used to
349
- override that. It will be given rectangles for the list of
350
- completions, the selected option, the info element, and the
351
- availble [tooltip space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace),
352
- and should return style and/or class strings for the info
353
- element.
347
+ By default, [info](https://codemirror.net/6/docs/ref/#autocomplete.Completion.info) tooltips are
348
+ placed to the side of the selected completion. This option can
349
+ be used to override that. It will be given rectangles for the
350
+ list of completions, the selected option, the info element, and
351
+ the availble [tooltip
352
+ space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace), and should return
353
+ style and/or class strings for the info element.
354
354
  */
355
355
  positionInfo?: (view: EditorView, list: Rect, option: Rect, info: Rect, space: Rect) => {
356
356
  style?: string;
package/dist/index.d.ts CHANGED
@@ -9,7 +9,7 @@ Objects type used to represent individual completions.
9
9
  interface Completion {
10
10
  /**
11
11
  The label to show in the completion picker. This is what input
12
- is matched agains to determine whether a completion matches (and
12
+ is matched against to determine whether a completion matches (and
13
13
  how well it matches).
14
14
  */
15
15
  label: string;
@@ -75,7 +75,7 @@ The type returned from
75
75
  node, null to indicate there is no info, or an object with an
76
76
  optional `destroy` method that cleans up the node.
77
77
  */
78
- declare type CompletionInfo = Node | null | {
78
+ type CompletionInfo = Node | null | {
79
79
  dom: Node;
80
80
  destroy?(): void;
81
81
  };
@@ -196,7 +196,7 @@ may return its [result](https://codemirror.net/6/docs/ref/#autocomplete.Completi
196
196
  synchronously or as a promise. Returning null indicates no
197
197
  completions are available.
198
198
  */
199
- declare type CompletionSource = (context: CompletionContext) => CompletionResult | null | Promise<CompletionResult | null>;
199
+ type CompletionSource = (context: CompletionContext) => CompletionResult | null | Promise<CompletionResult | null>;
200
200
  /**
201
201
  Interface for objects returned by completion sources.
202
202
  */
@@ -344,13 +344,13 @@ interface CompletionConfig {
344
344
  position: number;
345
345
  }[];
346
346
  /**
347
- By default, [info](https://codemirror.net/6/docs/ref/#autocomplet.Completion.info) tooltips are
348
- placed to the side of the selected. This option can be used to
349
- override that. It will be given rectangles for the list of
350
- completions, the selected option, the info element, and the
351
- availble [tooltip space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace),
352
- and should return style and/or class strings for the info
353
- element.
347
+ By default, [info](https://codemirror.net/6/docs/ref/#autocomplete.Completion.info) tooltips are
348
+ placed to the side of the selected completion. This option can
349
+ be used to override that. It will be given rectangles for the
350
+ list of completions, the selected option, the info element, and
351
+ the availble [tooltip
352
+ space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace), and should return
353
+ style and/or class strings for the info element.
354
354
  */
355
355
  positionInfo?: (view: EditorView, list: Rect, option: Rect, info: Rect, space: Rect) => {
356
356
  style?: string;
package/dist/index.js CHANGED
@@ -171,7 +171,7 @@ function insertCompletionText(state, text, from, to) {
171
171
  changes: { from: range.from + fromOff, to: to == main.from ? range.to : range.from + toOff, insert: text },
172
172
  range: EditorSelection.cursor(range.from + fromOff + text.length)
173
173
  };
174
- })), { userEvent: "input.complete" });
174
+ })), { scrollIntoView: true, userEvent: "input.complete" });
175
175
  }
176
176
  const SourceCache = /*@__PURE__*/new WeakMap();
177
177
  function asSource(source) {
@@ -223,7 +223,7 @@ class FuzzyMatcher {
223
223
  // is. See `Penalty` above.
224
224
  match(word) {
225
225
  if (this.pattern.length == 0)
226
- return this.ret(-100 /* NotFull */, []);
226
+ return this.ret(-100 /* Penalty.NotFull */, []);
227
227
  if (word.length < this.pattern.length)
228
228
  return false;
229
229
  let { chars, folded, any, precise, byWord } = this;
@@ -231,17 +231,17 @@ class FuzzyMatcher {
231
231
  // at the start
232
232
  if (chars.length == 1) {
233
233
  let first = codePointAt(word, 0), firstSize = codePointSize(first);
234
- let score = firstSize == word.length ? 0 : -100 /* NotFull */;
234
+ let score = firstSize == word.length ? 0 : -100 /* Penalty.NotFull */;
235
235
  if (first == chars[0]) ;
236
236
  else if (first == folded[0])
237
- score += -200 /* CaseFold */;
237
+ score += -200 /* Penalty.CaseFold */;
238
238
  else
239
239
  return false;
240
240
  return this.ret(score, [0, firstSize]);
241
241
  }
242
242
  let direct = word.indexOf(this.pattern);
243
243
  if (direct == 0)
244
- return this.ret(word.length == this.pattern.length ? 0 : -100 /* NotFull */, [0, this.pattern.length]);
244
+ return this.ret(word.length == this.pattern.length ? 0 : -100 /* Penalty.NotFull */, [0, this.pattern.length]);
245
245
  let len = chars.length, anyTo = 0;
246
246
  if (direct < 0) {
247
247
  for (let i = 0, e = Math.min(word.length, 200); i < e && anyTo < len;) {
@@ -265,7 +265,7 @@ class FuzzyMatcher {
265
265
  let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
266
266
  let hasLower = /[a-z]/.test(word), wordAdjacent = true;
267
267
  // Go over the option's text, scanning for the various kinds of matches
268
- for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* NonWord */; i < e && byWordTo < len;) {
268
+ for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* Tp.NonWord */; i < e && byWordTo < len;) {
269
269
  let next = codePointAt(word, i);
270
270
  if (direct < 0) {
271
271
  if (preciseTo < len && next == chars[preciseTo])
@@ -283,9 +283,9 @@ class FuzzyMatcher {
283
283
  }
284
284
  }
285
285
  let ch, type = next < 0xff
286
- ? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Lower */ : next >= 65 && next <= 90 ? 1 /* Upper */ : 0 /* NonWord */)
287
- : ((ch = fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Upper */ : ch != ch.toUpperCase() ? 2 /* Lower */ : 0 /* NonWord */);
288
- if (!i || type == 1 /* Upper */ && hasLower || prevType == 0 /* NonWord */ && type != 0 /* NonWord */) {
286
+ ? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Tp.Lower */ : next >= 65 && next <= 90 ? 1 /* Tp.Upper */ : 0 /* Tp.NonWord */)
287
+ : ((ch = fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Tp.Upper */ : ch != ch.toUpperCase() ? 2 /* Tp.Lower */ : 0 /* Tp.NonWord */);
288
+ if (!i || type == 1 /* Tp.Upper */ && hasLower || prevType == 0 /* Tp.NonWord */ && type != 0 /* Tp.NonWord */) {
289
289
  if (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
290
290
  byWord[byWordTo++] = i;
291
291
  else if (byWord.length)
@@ -295,18 +295,18 @@ class FuzzyMatcher {
295
295
  i += codePointSize(next);
296
296
  }
297
297
  if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
298
- return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0), byWord, word);
298
+ return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0), byWord, word);
299
299
  if (adjacentTo == len && adjacentStart == 0)
300
- return this.ret(-200 /* CaseFold */ - word.length + (adjacentEnd == word.length ? 0 : -100 /* NotFull */), [0, adjacentEnd]);
300
+ return this.ret(-200 /* Penalty.CaseFold */ - word.length + (adjacentEnd == word.length ? 0 : -100 /* Penalty.NotFull */), [0, adjacentEnd]);
301
301
  if (direct > -1)
302
- return this.ret(-700 /* NotStart */ - word.length, [direct, direct + this.pattern.length]);
302
+ return this.ret(-700 /* Penalty.NotStart */ - word.length, [direct, direct + this.pattern.length]);
303
303
  if (adjacentTo == len)
304
- return this.ret(-200 /* CaseFold */ + -700 /* NotStart */ - word.length, [adjacentStart, adjacentEnd]);
304
+ return this.ret(-200 /* Penalty.CaseFold */ + -700 /* Penalty.NotStart */ - word.length, [adjacentStart, adjacentEnd]);
305
305
  if (byWordTo == len)
306
- return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */ +
307
- (wordAdjacent ? 0 : -1100 /* Gap */), byWord, word);
306
+ return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0) + -700 /* Penalty.NotStart */ +
307
+ (wordAdjacent ? 0 : -1100 /* Penalty.Gap */), byWord, word);
308
308
  return chars.length == 2 ? false
309
- : this.result((any[0] ? -700 /* NotStart */ : 0) + -200 /* CaseFold */ + -1100 /* Gap */, any, word);
309
+ : this.result((any[0] ? -700 /* Penalty.NotStart */ : 0) + -200 /* Penalty.CaseFold */ + -1100 /* Penalty.Gap */, any, word);
310
310
  }
311
311
  result(score, positions, word) {
312
312
  let result = [], i = 0;
@@ -353,7 +353,7 @@ const completionConfig = /*@__PURE__*/Facet.define({
353
353
  function joinClass(a, b) {
354
354
  return a ? b ? a + " " + b : a : b;
355
355
  }
356
- function defaultPositionInfo(view, list, option, info, space) {
356
+ function defaultPositionInfo(view, list, option, info, space, tooltip) {
357
357
  let rtl = view.textDirection == Direction.RTL, left = rtl, narrow = false;
358
358
  let side = "top", offset, maxWidth;
359
359
  let spaceLeft = list.left - space.left, spaceRight = space.right - list.right;
@@ -364,11 +364,11 @@ function defaultPositionInfo(view, list, option, info, space) {
364
364
  left = true;
365
365
  if (infoWidth <= (left ? spaceLeft : spaceRight)) {
366
366
  offset = Math.max(space.top, Math.min(option.top, space.bottom - infoHeight)) - list.top;
367
- maxWidth = Math.min(400 /* Width */, left ? spaceLeft : spaceRight);
367
+ maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight);
368
368
  }
369
369
  else {
370
370
  narrow = true;
371
- maxWidth = Math.min(400 /* Width */, (rtl ? list.right : space.right - list.left) - 30 /* Margin */);
371
+ maxWidth = Math.min(400 /* Info.Width */, (rtl ? list.right : space.right - list.left) - 30 /* Info.Margin */);
372
372
  let spaceBelow = space.bottom - list.bottom;
373
373
  if (spaceBelow >= infoHeight || spaceBelow > list.top) { // Below the completion
374
374
  offset = option.bottom - list.top;
@@ -378,8 +378,10 @@ function defaultPositionInfo(view, list, option, info, space) {
378
378
  offset = list.bottom - option.top;
379
379
  }
380
380
  }
381
+ let scaleY = (list.bottom - list.top) / tooltip.offsetHeight;
382
+ let scaleX = (list.right - list.left) / tooltip.offsetWidth;
381
383
  return {
382
- style: `${side}: ${offset}px; max-width: ${maxWidth}px`,
384
+ style: `${side}: ${offset / scaleY}px; max-width: ${maxWidth / scaleX}px`,
383
385
  class: "cm-completionInfo-" + (narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right")
384
386
  };
385
387
  }
@@ -481,22 +483,32 @@ class CompletionTooltip {
481
483
  e.relatedTarget != view.contentDOM)
482
484
  view.dispatch({ effects: closeCompletionEffect.of(null) });
483
485
  });
484
- this.list = this.dom.appendChild(this.createListBox(options, cState.id, this.range));
486
+ this.showOptions(options, cState.id);
487
+ }
488
+ mount() { this.updateSel(); }
489
+ showOptions(options, id) {
490
+ if (this.list)
491
+ this.list.remove();
492
+ this.list = this.dom.appendChild(this.createListBox(options, id, this.range));
485
493
  this.list.addEventListener("scroll", () => {
486
494
  if (this.info)
487
495
  this.view.requestMeasure(this.placeInfoReq);
488
496
  });
489
497
  }
490
- mount() { this.updateSel(); }
491
498
  update(update) {
492
- var _a, _b, _c;
499
+ var _a;
493
500
  let cState = update.state.field(this.stateField);
494
501
  let prevState = update.startState.field(this.stateField);
495
502
  this.updateTooltipClass(update.state);
496
503
  if (cState != prevState) {
504
+ let { options, selected, disabled } = cState.open;
505
+ if (!prevState.open || prevState.open.options != options) {
506
+ this.range = rangeAroundSelected(options.length, selected, update.state.facet(completionConfig).maxRenderedOptions);
507
+ this.showOptions(options, cState.id);
508
+ }
497
509
  this.updateSel();
498
- if (((_a = cState.open) === null || _a === void 0 ? void 0 : _a.disabled) != ((_b = prevState.open) === null || _b === void 0 ? void 0 : _b.disabled))
499
- this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!((_c = cState.open) === null || _c === void 0 ? void 0 : _c.disabled));
510
+ if (disabled != ((_a = prevState.open) === null || _a === void 0 ? void 0 : _a.disabled))
511
+ this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!disabled);
500
512
  }
501
513
  }
502
514
  updateTooltipClass(state) {
@@ -520,12 +532,7 @@ class CompletionTooltip {
520
532
  let cState = this.view.state.field(this.stateField), open = cState.open;
521
533
  if (open.selected > -1 && open.selected < this.range.from || open.selected >= this.range.to) {
522
534
  this.range = rangeAroundSelected(open.options.length, open.selected, this.view.state.facet(completionConfig).maxRenderedOptions);
523
- this.list.remove();
524
- this.list = this.dom.appendChild(this.createListBox(open.options, cState.id, this.range));
525
- this.list.addEventListener("scroll", () => {
526
- if (this.info)
527
- this.view.requestMeasure(this.placeInfoReq);
528
- });
535
+ this.showOptions(open.options, cState.id);
529
536
  }
530
537
  if (this.updateSelectedOption(open.selected)) {
531
538
  this.destroyInfo();
@@ -599,7 +606,7 @@ class CompletionTooltip {
599
606
  if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 ||
600
607
  selRect.bottom < Math.max(space.top, listRect.top) + 10)
601
608
  return null;
602
- return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space);
609
+ return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space, this.dom);
603
610
  }
604
611
  placeInfo(pos) {
605
612
  if (this.info) {
@@ -665,18 +672,17 @@ class CompletionTooltip {
665
672
  this.destroyInfo();
666
673
  }
667
674
  }
668
- // We allocate a new function instance every time the completion
669
- // changes to force redrawing/repositioning of the tooltip
670
675
  function completionTooltip(stateField, applyCompletion) {
671
676
  return (view) => new CompletionTooltip(view, stateField, applyCompletion);
672
677
  }
673
678
  function scrollIntoView(container, element) {
674
679
  let parent = container.getBoundingClientRect();
675
680
  let self = element.getBoundingClientRect();
681
+ let scaleY = parent.height / container.offsetHeight;
676
682
  if (self.top < parent.top)
677
- container.scrollTop -= parent.top - self.top;
683
+ container.scrollTop -= (parent.top - self.top) / scaleY;
678
684
  else if (self.bottom > parent.bottom)
679
- container.scrollTop += self.bottom - parent.bottom;
685
+ container.scrollTop += (self.bottom - parent.bottom) / scaleY;
680
686
  }
681
687
 
682
688
  // Used to pick a preferred option when two options with the same
@@ -759,7 +765,7 @@ class CompletionDialog {
759
765
  static build(active, state, id, prev, conf) {
760
766
  let options = sortOptions(active, state);
761
767
  if (!options.length) {
762
- return prev && active.some(a => a.state == 1 /* Pending */) ?
768
+ return prev && active.some(a => a.state == 1 /* State.Pending */) ?
763
769
  new CompletionDialog(prev.options, prev.attrs, prev.tooltip, prev.timestamp, prev.selected, true) : null;
764
770
  }
765
771
  let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
@@ -773,7 +779,7 @@ class CompletionDialog {
773
779
  }
774
780
  return new CompletionDialog(options, makeAttrs(id, selected), {
775
781
  pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
776
- create: completionTooltip(completionState, applyCompletion),
782
+ create: createTooltip,
777
783
  above: conf.aboveCursor,
778
784
  }, prev ? prev.timestamp : Date.now(), selected, false);
779
785
  }
@@ -796,7 +802,7 @@ class CompletionState {
796
802
  state.languageDataAt("autocomplete", cur(state)).map(asSource);
797
803
  let active = sources.map(source => {
798
804
  let value = this.active.find(s => s.source == source) ||
799
- new ActiveSource(source, this.active.some(a => a.state != 0 /* Inactive */) ? 1 /* Pending */ : 0 /* Inactive */);
805
+ new ActiveSource(source, this.active.some(a => a.state != 0 /* State.Inactive */) ? 1 /* State.Pending */ : 0 /* State.Inactive */);
800
806
  return value.update(tr, conf);
801
807
  });
802
808
  if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
@@ -807,10 +813,10 @@ class CompletionState {
807
813
  if (tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
808
814
  !sameResults(active, this.active))
809
815
  open = CompletionDialog.build(active, state, this.id, open, conf);
810
- else if (open && open.disabled && !active.some(a => a.state == 1 /* Pending */))
816
+ else if (open && open.disabled && !active.some(a => a.state == 1 /* State.Pending */))
811
817
  open = null;
812
- if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
813
- active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
818
+ if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult()))
819
+ active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* State.Inactive */) : a);
814
820
  for (let effect of tr.effects)
815
821
  if (effect.is(setSelectedEffect))
816
822
  open = open && open.setSelected(effect.value, this.id);
@@ -864,13 +870,13 @@ class ActiveSource {
864
870
  value = value.handleUserEvent(tr, event, conf);
865
871
  else if (tr.docChanged)
866
872
  value = value.handleChange(tr);
867
- else if (tr.selection && value.state != 0 /* Inactive */)
868
- value = new ActiveSource(value.source, 0 /* Inactive */);
873
+ else if (tr.selection && value.state != 0 /* State.Inactive */)
874
+ value = new ActiveSource(value.source, 0 /* State.Inactive */);
869
875
  for (let effect of tr.effects) {
870
876
  if (effect.is(startCompletionEffect))
871
- value = new ActiveSource(value.source, 1 /* Pending */, effect.value ? cur(tr.state) : -1);
877
+ value = new ActiveSource(value.source, 1 /* State.Pending */, effect.value ? cur(tr.state) : -1);
872
878
  else if (effect.is(closeCompletionEffect))
873
- value = new ActiveSource(value.source, 0 /* Inactive */);
879
+ value = new ActiveSource(value.source, 0 /* State.Inactive */);
874
880
  else if (effect.is(setActiveEffect))
875
881
  for (let active of effect.value)
876
882
  if (active.source == value.source)
@@ -879,10 +885,10 @@ class ActiveSource {
879
885
  return value;
880
886
  }
881
887
  handleUserEvent(tr, type, conf) {
882
- return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* Pending */);
888
+ return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* State.Pending */);
883
889
  }
884
890
  handleChange(tr) {
885
- return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
891
+ return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
886
892
  }
887
893
  map(changes) {
888
894
  return changes.empty || this.explicitPos < 0 ? this : new ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos));
@@ -890,7 +896,7 @@ class ActiveSource {
890
896
  }
891
897
  class ActiveResult extends ActiveSource {
892
898
  constructor(source, explicitPos, result, from, to) {
893
- super(source, 2 /* Result */, explicitPos);
899
+ super(source, 2 /* State.Result */, explicitPos);
894
900
  this.result = result;
895
901
  this.from = from;
896
902
  this.to = to;
@@ -903,17 +909,17 @@ class ActiveResult extends ActiveSource {
903
909
  if ((this.explicitPos < 0 ? pos <= from : pos < this.from) ||
904
910
  pos > to ||
905
911
  type == "delete" && cur(tr.startState) == this.from)
906
- return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */);
912
+ return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* State.Pending */ : 0 /* State.Inactive */);
907
913
  let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos), updated;
908
914
  if (checkValid(this.result.validFor, tr.state, from, to))
909
915
  return new ActiveResult(this.source, explicitPos, this.result, from, to);
910
916
  if (this.result.update &&
911
917
  (updated = this.result.update(this.result, from, to, new CompletionContext(tr.state, pos, explicitPos >= 0))))
912
918
  return new ActiveResult(this.source, explicitPos, updated, updated.from, (_a = updated.to) !== null && _a !== void 0 ? _a : cur(tr.state));
913
- return new ActiveSource(this.source, 1 /* Pending */, explicitPos);
919
+ return new ActiveSource(this.source, 1 /* State.Pending */, explicitPos);
914
920
  }
915
921
  handleChange(tr) {
916
- return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
922
+ return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
917
923
  }
918
924
  map(mapping) {
919
925
  return mapping.empty ? this :
@@ -949,6 +955,7 @@ function applyCompletion(view, option) {
949
955
  apply(view, option.completion, result.from, result.to);
950
956
  return true;
951
957
  }
958
+ const createTooltip = /*@__PURE__*/completionTooltip(completionState, applyCompletion);
952
959
 
953
960
  /**
954
961
  Returns a command that moves the completion selection forward or
@@ -999,7 +1006,7 @@ Close the currently active completion.
999
1006
  */
1000
1007
  const closeCompletion = (view) => {
1001
1008
  let cState = view.state.field(completionState, false);
1002
- if (!cState || !cState.active.some(a => a.state != 0 /* Inactive */))
1009
+ if (!cState || !cState.active.some(a => a.state != 0 /* State.Inactive */))
1003
1010
  return false;
1004
1011
  view.dispatch({ effects: closeCompletionEffect.of(null) });
1005
1012
  return true;
@@ -1022,9 +1029,9 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
1022
1029
  this.debounceUpdate = -1;
1023
1030
  this.running = [];
1024
1031
  this.debounceAccept = -1;
1025
- this.composing = 0 /* None */;
1032
+ this.composing = 0 /* CompositionState.None */;
1026
1033
  for (let active of view.state.field(completionState).active)
1027
- if (active.state == 1 /* Pending */)
1034
+ if (active.state == 1 /* State.Pending */)
1028
1035
  this.startQuery(active);
1029
1036
  }
1030
1037
  update(update) {
@@ -1055,21 +1062,21 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
1055
1062
  }
1056
1063
  if (this.debounceUpdate > -1)
1057
1064
  clearTimeout(this.debounceUpdate);
1058
- this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.active.source == a.source))
1065
+ this.debounceUpdate = cState.active.some(a => a.state == 1 /* State.Pending */ && !this.running.some(q => q.active.source == a.source))
1059
1066
  ? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
1060
- if (this.composing != 0 /* None */)
1067
+ if (this.composing != 0 /* CompositionState.None */)
1061
1068
  for (let tr of update.transactions) {
1062
1069
  if (getUserEvent(tr) == "input")
1063
- this.composing = 2 /* Changed */;
1064
- else if (this.composing == 2 /* Changed */ && tr.selection)
1065
- this.composing = 3 /* ChangedAndMoved */;
1070
+ this.composing = 2 /* CompositionState.Changed */;
1071
+ else if (this.composing == 2 /* CompositionState.Changed */ && tr.selection)
1072
+ this.composing = 3 /* CompositionState.ChangedAndMoved */;
1066
1073
  }
1067
1074
  }
1068
1075
  startUpdate() {
1069
1076
  this.debounceUpdate = -1;
1070
1077
  let { state } = this.view, cState = state.field(completionState);
1071
1078
  for (let active of cState.active) {
1072
- if (active.state == 1 /* Pending */ && !this.running.some(r => r.active.source == active.source))
1079
+ if (active.state == 1 /* State.Pending */ && !this.running.some(r => r.active.source == active.source))
1073
1080
  this.startQuery(active);
1074
1081
  }
1075
1082
  }
@@ -1120,14 +1127,14 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
1120
1127
  }
1121
1128
  }
1122
1129
  let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
1123
- if (current && current.state == 1 /* Pending */) {
1130
+ if (current && current.state == 1 /* State.Pending */) {
1124
1131
  if (query.done == null) {
1125
1132
  // Explicitly failed. Should clear the pending status if it
1126
1133
  // hasn't been re-set in the meantime.
1127
- let active = new ActiveSource(query.active.source, 0 /* Inactive */);
1134
+ let active = new ActiveSource(query.active.source, 0 /* State.Inactive */);
1128
1135
  for (let tr of query.updates)
1129
1136
  active = active.update(tr, conf);
1130
- if (active.state != 1 /* Pending */)
1137
+ if (active.state != 1 /* State.Pending */)
1131
1138
  updated.push(active);
1132
1139
  }
1133
1140
  else {
@@ -1150,15 +1157,15 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
1150
1157
  }
1151
1158
  },
1152
1159
  compositionstart() {
1153
- this.composing = 1 /* Started */;
1160
+ this.composing = 1 /* CompositionState.Started */;
1154
1161
  },
1155
1162
  compositionend() {
1156
- if (this.composing == 3 /* ChangedAndMoved */) {
1163
+ if (this.composing == 3 /* CompositionState.ChangedAndMoved */) {
1157
1164
  // Safari fires compositionend events synchronously, possibly
1158
1165
  // from inside an update, so dispatch asynchronously to avoid reentrancy
1159
1166
  setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
1160
1167
  }
1161
- this.composing = 0 /* None */;
1168
+ this.composing = 0 /* CompositionState.None */;
1162
1169
  }
1163
1170
  }
1164
1171
  });
@@ -1218,13 +1225,13 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
1218
1225
  position: "absolute",
1219
1226
  padding: "3px 9px",
1220
1227
  width: "max-content",
1221
- maxWidth: `${400 /* Width */}px`,
1228
+ maxWidth: `${400 /* Info.Width */}px`,
1222
1229
  boxSizing: "border-box"
1223
1230
  },
1224
1231
  ".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
1225
1232
  ".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
1226
- ".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Margin */}px` },
1227
- ".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Margin */}px` },
1233
+ ".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Info.Margin */}px` },
1234
+ ".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Info.Margin */}px` },
1228
1235
  "&light .cm-snippetField": { backgroundColor: "#00000022" },
1229
1236
  "&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
1230
1237
  ".cm-snippetFieldPosition": {
@@ -1475,7 +1482,8 @@ function moveField(dir) {
1475
1482
  let next = active.active + dir, last = dir > 0 && !active.ranges.some(r => r.field == next + dir);
1476
1483
  dispatch(state.update({
1477
1484
  selection: fieldSelection(active.ranges, next),
1478
- effects: setActive.of(last ? null : new ActiveSnippet(active.ranges, next))
1485
+ effects: setActive.of(last ? null : new ActiveSnippet(active.ranges, next)),
1486
+ scrollIntoView: true
1479
1487
  }));
1480
1488
  return true;
1481
1489
  };
@@ -1547,14 +1555,16 @@ const snippetPointerHandler = /*@__PURE__*/EditorView.domEventHandlers({
1547
1555
  return false;
1548
1556
  view.dispatch({
1549
1557
  selection: fieldSelection(active.ranges, match.field),
1550
- effects: setActive.of(active.ranges.some(r => r.field > match.field) ? new ActiveSnippet(active.ranges, match.field) : null)
1558
+ effects: setActive.of(active.ranges.some(r => r.field > match.field)
1559
+ ? new ActiveSnippet(active.ranges, match.field) : null),
1560
+ scrollIntoView: true
1551
1561
  });
1552
1562
  return true;
1553
1563
  }
1554
1564
  });
1555
1565
 
1556
1566
  function wordRE(wordChars) {
1557
- let escaped = wordChars.replace(/[\\[.+*?(){|^$]/g, "\\$&");
1567
+ let escaped = wordChars.replace(/[\]\-\\]/g, "\\$&");
1558
1568
  try {
1559
1569
  return new RegExp(`[\\p{Alphabetic}\\p{Number}_${escaped}]+`, "ug");
1560
1570
  }
@@ -1577,7 +1587,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
1577
1587
  if (!seen[m[0]] && pos + m.index != ignoreAt) {
1578
1588
  result.push({ type: "text", label: m[0] });
1579
1589
  seen[m[0]] = true;
1580
- if (result.length >= 2000 /* MaxList */)
1590
+ if (result.length >= 2000 /* C.MaxList */)
1581
1591
  return;
1582
1592
  }
1583
1593
  }
@@ -1585,7 +1595,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
1585
1595
  }
1586
1596
  }
1587
1597
  function collectWords(doc, cache, wordRE, to, ignoreAt) {
1588
- let big = doc.length >= 1000 /* MinCacheLen */;
1598
+ let big = doc.length >= 1000 /* C.MinCacheLen */;
1589
1599
  let cached = big && cache.get(doc);
1590
1600
  if (cached)
1591
1601
  return cached;
@@ -1593,7 +1603,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
1593
1603
  if (doc.children) {
1594
1604
  let pos = 0;
1595
1605
  for (let ch of doc.children) {
1596
- if (ch.length >= 1000 /* MinCacheLen */) {
1606
+ if (ch.length >= 1000 /* C.MinCacheLen */) {
1597
1607
  for (let c of collectWords(ch, cache, wordRE, to - pos, ignoreAt - pos)) {
1598
1608
  if (!seen[c.label]) {
1599
1609
  seen[c.label] = true;
@@ -1610,7 +1620,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
1610
1620
  else {
1611
1621
  storeWords(doc, wordRE, result, seen, ignoreAt);
1612
1622
  }
1613
- if (big && result.length < 2000 /* MaxList */)
1623
+ if (big && result.length < 2000 /* C.MaxList */)
1614
1624
  cache.set(doc, result);
1615
1625
  return result;
1616
1626
  }
@@ -1626,7 +1636,7 @@ const completeAnyWord = context => {
1626
1636
  if (!token && !context.explicit)
1627
1637
  return null;
1628
1638
  let from = token ? token.from : context.pos;
1629
- let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* Range */, from);
1639
+ let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* C.Range */, from);
1630
1640
  return { from, options, validFor: mapRE(re, s => "^" + s) };
1631
1641
  };
1632
1642
 
@@ -1918,8 +1928,8 @@ returns `null`.
1918
1928
  */
1919
1929
  function completionStatus(state) {
1920
1930
  let cState = state.field(completionState, false);
1921
- return cState && cState.active.some(a => a.state == 1 /* Pending */) ? "pending"
1922
- : cState && cState.active.some(a => a.state != 0 /* Inactive */) ? "active" : null;
1931
+ return cState && cState.active.some(a => a.state == 1 /* State.Pending */) ? "pending"
1932
+ : cState && cState.active.some(a => a.state != 0 /* State.Inactive */) ? "active" : null;
1923
1933
  }
1924
1934
  const completionArrayCache = /*@__PURE__*/new WeakMap;
1925
1935
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/autocomplete",
3
- "version": "6.9.0",
3
+ "version": "6.9.2",
4
4
  "description": "Autocompletion for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -28,7 +28,7 @@
28
28
  "dependencies": {
29
29
  "@codemirror/language": "^6.0.0",
30
30
  "@codemirror/state": "^6.0.0",
31
- "@codemirror/view": "^6.6.0",
31
+ "@codemirror/view": "^6.17.0",
32
32
  "@lezer/common": "^1.0.0"
33
33
  },
34
34
  "peerDependencies": {