@codemirror/autocomplete 6.0.3 → 6.1.1

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,25 @@
1
+ ## 6.1.1 (2022-09-08)
2
+
3
+ ### Bug fixes
4
+
5
+ Fix a bug that prevented transactions produced by `deleteBracketPair` from being marked as deletion user events.
6
+
7
+ Improve positioning of completion info tooltips so they are less likely to stick out of the screen on small displays.
8
+
9
+ ## 6.1.0 (2022-07-19)
10
+
11
+ ### New features
12
+
13
+ You can now provide a `compareCompletions` option to autocompletion to influence the way completions with the same match score are sorted.
14
+
15
+ The `selectOnOpen` option to autocompletion can be used to require explicitly selecting a completion option before `acceptCompletion` does anything.
16
+
17
+ ## 6.0.4 (2022-07-07)
18
+
19
+ ### Bug fixes
20
+
21
+ Remove a leftover `console.log` in bracket closing code.
22
+
1
23
  ## 6.0.3 (2022-07-04)
2
24
 
3
25
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -233,7 +233,7 @@ class FuzzyMatcher {
233
233
  if (chars.length == 1) {
234
234
  let first = state.codePointAt(word, 0);
235
235
  return first == chars[0] ? [0, 0, state.codePointSize(first)]
236
- : first == folded[0] ? [-200 /* CaseFold */, 0, state.codePointSize(first)] : null;
236
+ : first == folded[0] ? [-200 /* Penalty.CaseFold */, 0, state.codePointSize(first)] : null;
237
237
  }
238
238
  let direct = word.indexOf(this.pattern);
239
239
  if (direct == 0)
@@ -261,7 +261,7 @@ class FuzzyMatcher {
261
261
  let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
262
262
  let hasLower = /[a-z]/.test(word), wordAdjacent = true;
263
263
  // Go over the option's text, scanning for the various kinds of matches
264
- for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* NonWord */; i < e && byWordTo < len;) {
264
+ for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* Tp.NonWord */; i < e && byWordTo < len;) {
265
265
  let next = state.codePointAt(word, i);
266
266
  if (direct < 0) {
267
267
  if (preciseTo < len && next == chars[preciseTo])
@@ -279,9 +279,9 @@ class FuzzyMatcher {
279
279
  }
280
280
  }
281
281
  let ch, type = next < 0xff
282
- ? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Lower */ : next >= 65 && next <= 90 ? 1 /* Upper */ : 0 /* NonWord */)
283
- : ((ch = state.fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Upper */ : ch != ch.toUpperCase() ? 2 /* Lower */ : 0 /* NonWord */);
284
- if (!i || type == 1 /* Upper */ && hasLower || prevType == 0 /* NonWord */ && type != 0 /* NonWord */) {
282
+ ? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Tp.Lower */ : next >= 65 && next <= 90 ? 1 /* Tp.Upper */ : 0 /* Tp.NonWord */)
283
+ : ((ch = state.fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Tp.Upper */ : ch != ch.toUpperCase() ? 2 /* Tp.Lower */ : 0 /* Tp.NonWord */);
284
+ if (!i || type == 1 /* Tp.Upper */ && hasLower || prevType == 0 /* Tp.NonWord */ && type != 0 /* Tp.NonWord */) {
285
285
  if (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
286
286
  byWord[byWordTo++] = i;
287
287
  else if (byWord.length)
@@ -291,17 +291,17 @@ class FuzzyMatcher {
291
291
  i += state.codePointSize(next);
292
292
  }
293
293
  if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
294
- return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0), byWord, word);
294
+ return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0), byWord, word);
295
295
  if (adjacentTo == len && adjacentStart == 0)
296
- return [-200 /* CaseFold */ - word.length, 0, adjacentEnd];
296
+ return [-200 /* Penalty.CaseFold */ - word.length, 0, adjacentEnd];
297
297
  if (direct > -1)
298
- return [-700 /* NotStart */ - word.length, direct, direct + this.pattern.length];
298
+ return [-700 /* Penalty.NotStart */ - word.length, direct, direct + this.pattern.length];
299
299
  if (adjacentTo == len)
300
- return [-200 /* CaseFold */ + -700 /* NotStart */ - word.length, adjacentStart, adjacentEnd];
300
+ return [-200 /* Penalty.CaseFold */ + -700 /* Penalty.NotStart */ - word.length, adjacentStart, adjacentEnd];
301
301
  if (byWordTo == len)
302
- return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */ +
303
- (wordAdjacent ? 0 : -1100 /* Gap */), byWord, word);
304
- return chars.length == 2 ? null : this.result((any[0] ? -700 /* NotStart */ : 0) + -200 /* CaseFold */ + -1100 /* Gap */, any, word);
302
+ return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0) + -700 /* Penalty.NotStart */ +
303
+ (wordAdjacent ? 0 : -1100 /* Penalty.Gap */), byWord, word);
304
+ return chars.length == 2 ? null : this.result((any[0] ? -700 /* Penalty.NotStart */ : 0) + -200 /* Penalty.CaseFold */ + -1100 /* Penalty.Gap */, any, word);
305
305
  }
306
306
  result(score, positions, word) {
307
307
  let result = [score - word.length], i = 1;
@@ -322,6 +322,7 @@ const completionConfig = state.Facet.define({
322
322
  combine(configs) {
323
323
  return state.combineConfig(configs, {
324
324
  activateOnTyping: true,
325
+ selectOnOpen: true,
325
326
  override: null,
326
327
  closeOnBlur: true,
327
328
  maxRenderedOptions: 100,
@@ -329,7 +330,8 @@ const completionConfig = state.Facet.define({
329
330
  optionClass: () => "",
330
331
  aboveCursor: false,
331
332
  icons: true,
332
- addToOptions: []
333
+ addToOptions: [],
334
+ compareCompletions: (a, b) => a.label.localeCompare(b.label)
333
335
  }, {
334
336
  defaultKeymap: (a, b) => a && b,
335
337
  closeOnBlur: (a, b) => a && b,
@@ -392,6 +394,8 @@ function optionContent(config) {
392
394
  function rangeAroundSelected(total, selected, max) {
393
395
  if (total <= max)
394
396
  return { from: 0, to: total };
397
+ if (selected < 0)
398
+ selected = 0;
395
399
  if (selected <= (total >> 1)) {
396
400
  let off = Math.floor(selected / max);
397
401
  return { from: off * max, to: (off + 1) * max };
@@ -443,7 +447,7 @@ class CompletionTooltip {
443
447
  }
444
448
  updateSel() {
445
449
  let cState = this.view.state.field(this.stateField), open = cState.open;
446
- if (open.selected < this.range.from || open.selected >= this.range.to) {
450
+ if (open.selected > -1 && open.selected < this.range.from || open.selected >= this.range.to) {
447
451
  this.range = rangeAroundSelected(open.options.length, open.selected, this.view.state.facet(completionConfig).maxRenderedOptions);
448
452
  this.list.remove();
449
453
  this.list = this.dom.appendChild(this.createListBox(open.options, cState.id, this.range));
@@ -504,26 +508,47 @@ class CompletionTooltip {
504
508
  let sel = this.dom.querySelector("[aria-selected]");
505
509
  if (!sel || !this.info)
506
510
  return null;
511
+ let win = this.dom.ownerDocument.defaultView;
507
512
  let listRect = this.dom.getBoundingClientRect();
508
513
  let infoRect = this.info.getBoundingClientRect();
509
514
  let selRect = sel.getBoundingClientRect();
510
- if (selRect.top > Math.min(innerHeight, listRect.bottom) - 10 || selRect.bottom < Math.max(0, listRect.top) + 10)
515
+ if (selRect.top > Math.min(win.innerHeight, listRect.bottom) - 10 || selRect.bottom < Math.max(0, listRect.top) + 10)
511
516
  return null;
512
- let top = Math.max(0, Math.min(selRect.top, innerHeight - infoRect.height)) - listRect.top;
513
- let left = this.view.textDirection == view.Direction.RTL;
514
- let spaceLeft = listRect.left, spaceRight = innerWidth - listRect.right;
517
+ let rtl = this.view.textDirection == view.Direction.RTL, left = rtl, narrow = false, maxWidth;
518
+ let top = "", bottom = "";
519
+ let spaceLeft = listRect.left, spaceRight = win.innerWidth - listRect.right;
515
520
  if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
516
521
  left = false;
517
522
  else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
518
523
  left = true;
519
- return { top, left };
524
+ if (infoRect.width <= (left ? spaceLeft : spaceRight)) {
525
+ top = (Math.max(0, Math.min(selRect.top, win.innerHeight - infoRect.height)) - listRect.top) + "px";
526
+ maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight) + "px";
527
+ }
528
+ else {
529
+ narrow = true;
530
+ maxWidth = Math.min(400 /* Info.Width */, (rtl ? listRect.right : win.innerWidth - listRect.left) - 30 /* Info.Margin */) + "px";
531
+ let spaceBelow = win.innerHeight - listRect.bottom;
532
+ if (spaceBelow >= infoRect.height || spaceBelow > listRect.top) // Below the completion
533
+ top = (selRect.bottom - listRect.top) + "px";
534
+ else // Above it
535
+ bottom = (listRect.bottom - selRect.top) + "px";
536
+ }
537
+ return {
538
+ top, bottom, maxWidth,
539
+ class: narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right",
540
+ };
520
541
  }
521
542
  positionInfo(pos) {
522
543
  if (this.info) {
523
- this.info.style.top = (pos ? pos.top : -1e6) + "px";
524
544
  if (pos) {
525
- this.info.classList.toggle("cm-completionInfo-left", pos.left);
526
- this.info.classList.toggle("cm-completionInfo-right", !pos.left);
545
+ this.info.style.top = pos.top;
546
+ this.info.style.bottom = pos.bottom;
547
+ this.info.style.maxWidth = pos.maxWidth;
548
+ this.info.className = "cm-tooltip cm-completionInfo cm-completionInfo-" + pos.class;
549
+ }
550
+ else {
551
+ this.info.style.top = "-1e6px";
527
552
  }
528
553
  }
529
554
  }
@@ -599,7 +624,8 @@ function sortOptions(active, state) {
599
624
  }
600
625
  }
601
626
  let result = [], prev = null;
602
- for (let opt of options.sort(cmpOption)) {
627
+ let compare = state.facet(completionConfig).compareCompletions;
628
+ for (let opt of options.sort((a, b) => (b.match[0] - a.match[0]) || compare(a.completion, b.completion))) {
603
629
  if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail ||
604
630
  (prev.type != null && opt.completion.type != null && prev.type != opt.completion.type) ||
605
631
  prev.apply != opt.completion.apply)
@@ -626,8 +652,8 @@ class CompletionDialog {
626
652
  let options = sortOptions(active, state);
627
653
  if (!options.length)
628
654
  return null;
629
- let selected = 0;
630
- if (prev && prev.selected) {
655
+ let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
656
+ if (prev && prev.selected != selected && prev.selected != -1) {
631
657
  let selectedValue = prev.options[prev.selected].completion;
632
658
  for (let i = 0; i < options.length; i++)
633
659
  if (options[i].completion == selectedValue) {
@@ -660,7 +686,7 @@ class CompletionState {
660
686
  state.languageDataAt("autocomplete", cur(state)).map(asSource);
661
687
  let active = sources.map(source => {
662
688
  let value = this.active.find(s => s.source == source) ||
663
- new ActiveSource(source, this.active.some(a => a.state != 0 /* Inactive */) ? 1 /* Pending */ : 0 /* Inactive */);
689
+ new ActiveSource(source, this.active.some(a => a.state != 0 /* State.Inactive */) ? 1 /* State.Pending */ : 0 /* State.Inactive */);
664
690
  return value.update(tr, conf);
665
691
  });
666
692
  if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
@@ -668,8 +694,8 @@ class CompletionState {
668
694
  let open = tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
669
695
  !sameResults(active, this.active) ? CompletionDialog.build(active, state, this.id, this.open, conf)
670
696
  : this.open && tr.docChanged ? this.open.map(tr.changes) : this.open;
671
- if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
672
- active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
697
+ if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult()))
698
+ active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* State.Inactive */) : a);
673
699
  for (let effect of tr.effects)
674
700
  if (effect.is(setSelectedEffect))
675
701
  open = open && open.setSelected(effect.value, this.id);
@@ -697,20 +723,16 @@ const baseAttrs = {
697
723
  "aria-autocomplete": "list"
698
724
  };
699
725
  function makeAttrs(id, selected) {
700
- return {
726
+ let result = {
701
727
  "aria-autocomplete": "list",
702
728
  "aria-haspopup": "listbox",
703
- "aria-activedescendant": id + "-" + selected,
704
729
  "aria-controls": id
705
730
  };
731
+ if (selected > -1)
732
+ result["aria-activedescendant"] = id + "-" + selected;
733
+ return result;
706
734
  }
707
735
  const none = [];
708
- function cmpOption(a, b) {
709
- let dScore = b.match[0] - a.match[0];
710
- if (dScore)
711
- return dScore;
712
- return a.completion.label.localeCompare(b.completion.label);
713
- }
714
736
  function getUserEvent(tr) {
715
737
  return tr.isUserEvent("input.type") ? "input" : tr.isUserEvent("delete.backward") ? "delete" : null;
716
738
  }
@@ -727,13 +749,13 @@ class ActiveSource {
727
749
  value = value.handleUserEvent(tr, event, conf);
728
750
  else if (tr.docChanged)
729
751
  value = value.handleChange(tr);
730
- else if (tr.selection && value.state != 0 /* Inactive */)
731
- value = new ActiveSource(value.source, 0 /* Inactive */);
752
+ else if (tr.selection && value.state != 0 /* State.Inactive */)
753
+ value = new ActiveSource(value.source, 0 /* State.Inactive */);
732
754
  for (let effect of tr.effects) {
733
755
  if (effect.is(startCompletionEffect))
734
- value = new ActiveSource(value.source, 1 /* Pending */, effect.value ? cur(tr.state) : -1);
756
+ value = new ActiveSource(value.source, 1 /* State.Pending */, effect.value ? cur(tr.state) : -1);
735
757
  else if (effect.is(closeCompletionEffect))
736
- value = new ActiveSource(value.source, 0 /* Inactive */);
758
+ value = new ActiveSource(value.source, 0 /* State.Inactive */);
737
759
  else if (effect.is(setActiveEffect))
738
760
  for (let active of effect.value)
739
761
  if (active.source == value.source)
@@ -742,10 +764,10 @@ class ActiveSource {
742
764
  return value;
743
765
  }
744
766
  handleUserEvent(tr, type, conf) {
745
- return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* Pending */);
767
+ return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* State.Pending */);
746
768
  }
747
769
  handleChange(tr) {
748
- return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
770
+ return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
749
771
  }
750
772
  map(changes) {
751
773
  return changes.empty || this.explicitPos < 0 ? this : new ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos));
@@ -753,7 +775,7 @@ class ActiveSource {
753
775
  }
754
776
  class ActiveResult extends ActiveSource {
755
777
  constructor(source, explicitPos, result, from, to) {
756
- super(source, 2 /* Result */, explicitPos);
778
+ super(source, 2 /* State.Result */, explicitPos);
757
779
  this.result = result;
758
780
  this.from = from;
759
781
  this.to = to;
@@ -766,17 +788,17 @@ class ActiveResult extends ActiveSource {
766
788
  if ((this.explicitPos < 0 ? pos <= from : pos < this.from) ||
767
789
  pos > to ||
768
790
  type == "delete" && cur(tr.startState) == this.from)
769
- return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */);
791
+ return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* State.Pending */ : 0 /* State.Inactive */);
770
792
  let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos), updated;
771
793
  if (checkValid(this.result.validFor, tr.state, from, to))
772
794
  return new ActiveResult(this.source, explicitPos, this.result, from, to);
773
795
  if (this.result.update &&
774
796
  (updated = this.result.update(this.result, from, to, new CompletionContext(tr.state, pos, explicitPos >= 0))))
775
797
  return new ActiveResult(this.source, explicitPos, updated, updated.from, (_a = updated.to) !== null && _a !== void 0 ? _a : cur(tr.state));
776
- return new ActiveSource(this.source, 1 /* Pending */, explicitPos);
798
+ return new ActiveSource(this.source, 1 /* State.Pending */, explicitPos);
777
799
  }
778
800
  handleChange(tr) {
779
- return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
801
+ return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
780
802
  }
781
803
  map(mapping) {
782
804
  return mapping.empty ? this :
@@ -818,7 +840,8 @@ function moveCompletionSelection(forward, by = "option") {
818
840
  if (by == "page" && (tooltip = view.getTooltip(view$1, cState.open.tooltip)))
819
841
  step = Math.max(2, Math.floor(tooltip.dom.offsetHeight /
820
842
  tooltip.dom.querySelector("li").offsetHeight) - 1);
821
- let selected = cState.open.selected + step * (forward ? 1 : -1), { length } = cState.open.options;
843
+ let { length } = cState.open.options;
844
+ let selected = cState.open.selected > -1 ? cState.open.selected + step * (forward ? 1 : -1) : forward ? 0 : length - 1;
822
845
  if (selected < 0)
823
846
  selected = by == "page" ? 0 : length - 1;
824
847
  else if (selected >= length)
@@ -832,7 +855,8 @@ Accept the current completion.
832
855
  */
833
856
  const acceptCompletion = (view) => {
834
857
  let cState = view.state.field(completionState, false);
835
- if (view.state.readOnly || !cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin)
858
+ if (view.state.readOnly || !cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin ||
859
+ cState.open.selected < 0)
836
860
  return false;
837
861
  applyCompletion(view, cState.open.options[cState.open.selected]);
838
862
  return true;
@@ -852,7 +876,7 @@ Close the currently active completion.
852
876
  */
853
877
  const closeCompletion = (view) => {
854
878
  let cState = view.state.field(completionState, false);
855
- if (!cState || !cState.active.some(a => a.state != 0 /* Inactive */))
879
+ if (!cState || !cState.active.some(a => a.state != 0 /* State.Inactive */))
856
880
  return false;
857
881
  view.dispatch({ effects: closeCompletionEffect.of(null) });
858
882
  return true;
@@ -875,9 +899,9 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
875
899
  this.debounceUpdate = -1;
876
900
  this.running = [];
877
901
  this.debounceAccept = -1;
878
- this.composing = 0 /* None */;
902
+ this.composing = 0 /* CompositionState.None */;
879
903
  for (let active of view.state.field(completionState).active)
880
- if (active.state == 1 /* Pending */)
904
+ if (active.state == 1 /* State.Pending */)
881
905
  this.startQuery(active);
882
906
  }
883
907
  update(update) {
@@ -908,21 +932,21 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
908
932
  }
909
933
  if (this.debounceUpdate > -1)
910
934
  clearTimeout(this.debounceUpdate);
911
- this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.active.source == a.source))
935
+ this.debounceUpdate = cState.active.some(a => a.state == 1 /* State.Pending */ && !this.running.some(q => q.active.source == a.source))
912
936
  ? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
913
- if (this.composing != 0 /* None */)
937
+ if (this.composing != 0 /* CompositionState.None */)
914
938
  for (let tr of update.transactions) {
915
939
  if (getUserEvent(tr) == "input")
916
- this.composing = 2 /* Changed */;
917
- else if (this.composing == 2 /* Changed */ && tr.selection)
918
- this.composing = 3 /* ChangedAndMoved */;
940
+ this.composing = 2 /* CompositionState.Changed */;
941
+ else if (this.composing == 2 /* CompositionState.Changed */ && tr.selection)
942
+ this.composing = 3 /* CompositionState.ChangedAndMoved */;
919
943
  }
920
944
  }
921
945
  startUpdate() {
922
946
  this.debounceUpdate = -1;
923
947
  let { state } = this.view, cState = state.field(completionState);
924
948
  for (let active of cState.active) {
925
- if (active.state == 1 /* Pending */ && !this.running.some(r => r.active.source == active.source))
949
+ if (active.state == 1 /* State.Pending */ && !this.running.some(r => r.active.source == active.source))
926
950
  this.startQuery(active);
927
951
  }
928
952
  }
@@ -973,14 +997,14 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
973
997
  }
974
998
  }
975
999
  let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
976
- if (current && current.state == 1 /* Pending */) {
1000
+ if (current && current.state == 1 /* State.Pending */) {
977
1001
  if (query.done == null) {
978
1002
  // Explicitly failed. Should clear the pending status if it
979
1003
  // hasn't been re-set in the meantime.
980
- let active = new ActiveSource(query.active.source, 0 /* Inactive */);
1004
+ let active = new ActiveSource(query.active.source, 0 /* State.Inactive */);
981
1005
  for (let tr of query.updates)
982
1006
  active = active.update(tr, conf);
983
- if (active.state != 1 /* Pending */)
1007
+ if (active.state != 1 /* State.Pending */)
984
1008
  updated.push(active);
985
1009
  }
986
1010
  else {
@@ -1000,15 +1024,15 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
1000
1024
  this.view.dispatch({ effects: closeCompletionEffect.of(null) });
1001
1025
  },
1002
1026
  compositionstart() {
1003
- this.composing = 1 /* Started */;
1027
+ this.composing = 1 /* CompositionState.Started */;
1004
1028
  },
1005
1029
  compositionend() {
1006
- if (this.composing == 3 /* ChangedAndMoved */) {
1030
+ if (this.composing == 3 /* CompositionState.ChangedAndMoved */) {
1007
1031
  // Safari fires compositionend events synchronously, possibly
1008
1032
  // from inside an update, so dispatch asynchronously to avoid reentrancy
1009
1033
  setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
1010
1034
  }
1011
- this.composing = 0 /* None */;
1035
+ this.composing = 0 /* CompositionState.None */;
1012
1036
  }
1013
1037
  }
1014
1038
  });
@@ -1053,10 +1077,13 @@ const baseTheme = view.EditorView.baseTheme({
1053
1077
  position: "absolute",
1054
1078
  padding: "3px 9px",
1055
1079
  width: "max-content",
1056
- maxWidth: "300px",
1080
+ maxWidth: `${400 /* Info.Width */}px`,
1081
+ boxSizing: "border-box"
1057
1082
  },
1058
1083
  ".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
1059
1084
  ".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
1085
+ ".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Info.Margin */}px` },
1086
+ ".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Info.Margin */}px` },
1060
1087
  "&light .cm-snippetField": { backgroundColor: "#00000022" },
1061
1088
  "&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
1062
1089
  ".cm-snippetFieldPosition": {
@@ -1391,7 +1418,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
1391
1418
  if (!seen[m[0]] && pos + m.index != ignoreAt) {
1392
1419
  result.push({ type: "text", label: m[0] });
1393
1420
  seen[m[0]] = true;
1394
- if (result.length >= 2000 /* MaxList */)
1421
+ if (result.length >= 2000 /* C.MaxList */)
1395
1422
  return;
1396
1423
  }
1397
1424
  }
@@ -1399,7 +1426,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
1399
1426
  }
1400
1427
  }
1401
1428
  function collectWords(doc, cache, wordRE, to, ignoreAt) {
1402
- let big = doc.length >= 1000 /* MinCacheLen */;
1429
+ let big = doc.length >= 1000 /* C.MinCacheLen */;
1403
1430
  let cached = big && cache.get(doc);
1404
1431
  if (cached)
1405
1432
  return cached;
@@ -1407,7 +1434,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
1407
1434
  if (doc.children) {
1408
1435
  let pos = 0;
1409
1436
  for (let ch of doc.children) {
1410
- if (ch.length >= 1000 /* MinCacheLen */) {
1437
+ if (ch.length >= 1000 /* C.MinCacheLen */) {
1411
1438
  for (let c of collectWords(ch, cache, wordRE, to - pos, ignoreAt - pos)) {
1412
1439
  if (!seen[c.label]) {
1413
1440
  seen[c.label] = true;
@@ -1424,7 +1451,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
1424
1451
  else {
1425
1452
  storeWords(doc, wordRE, result, seen, ignoreAt);
1426
1453
  }
1427
- if (big && result.length < 2000 /* MaxList */)
1454
+ if (big && result.length < 2000 /* C.MaxList */)
1428
1455
  cache.set(doc, result);
1429
1456
  return result;
1430
1457
  }
@@ -1440,7 +1467,7 @@ const completeAnyWord = context => {
1440
1467
  if (!token && !context.explicit)
1441
1468
  return null;
1442
1469
  let from = token ? token.from : context.pos;
1443
- let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* Range */, from);
1470
+ let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* C.Range */, from);
1444
1471
  return { from, options, validFor: mapRE(re, s => "^" + s) };
1445
1472
  };
1446
1473
 
@@ -1529,14 +1556,13 @@ const deleteBracketPair = ({ state: state$1, dispatch }) => {
1529
1556
  for (let token of tokens) {
1530
1557
  if (token == before && nextChar(state$1.doc, range.head) == closing(state.codePointAt(token, 0)))
1531
1558
  return { changes: { from: range.head - token.length, to: range.head + token.length },
1532
- range: state.EditorSelection.cursor(range.head - token.length),
1533
- userEvent: "delete.backward" };
1559
+ range: state.EditorSelection.cursor(range.head - token.length) };
1534
1560
  }
1535
1561
  }
1536
1562
  return { range: dont = range };
1537
1563
  });
1538
1564
  if (!dont)
1539
- dispatch(state$1.update(changes, { scrollIntoView: true }));
1565
+ dispatch(state$1.update(changes, { scrollIntoView: true, userEvent: "delete.backward" }));
1540
1566
  return !dont;
1541
1567
  };
1542
1568
  /**
@@ -1662,7 +1688,6 @@ function nodeStart(state, pos) {
1662
1688
  return tree.parent && tree.from == pos;
1663
1689
  }
1664
1690
  function probablyInString(state, pos, quoteToken) {
1665
- console.log('check pos', pos);
1666
1691
  let node = language.syntaxTree(state).resolveInner(pos, -1);
1667
1692
  for (let i = 0; i < 5; i++) {
1668
1693
  if (state.sliceDoc(node.from, node.from + quoteToken.length) == quoteToken) {
@@ -1723,8 +1748,8 @@ returns `null`.
1723
1748
  */
1724
1749
  function completionStatus(state) {
1725
1750
  let cState = state.field(completionState, false);
1726
- return cState && cState.active.some(a => a.state == 1 /* Pending */) ? "pending"
1727
- : cState && cState.active.some(a => a.state != 0 /* Inactive */) ? "active" : null;
1751
+ return cState && cState.active.some(a => a.state == 1 /* State.Pending */) ? "pending"
1752
+ : cState && cState.active.some(a => a.state != 0 /* State.Inactive */) ? "active" : null;
1728
1753
  }
1729
1754
  const completionArrayCache = new WeakMap;
1730
1755
  /**
@@ -1746,7 +1771,7 @@ Return the currently selected completion, if any.
1746
1771
  function selectedCompletion(state) {
1747
1772
  var _a;
1748
1773
  let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
1749
- return open ? open.options[open.selected].completion : null;
1774
+ return open && open.selected >= 0 ? open.options[open.selected].completion : null;
1750
1775
  }
1751
1776
  /**
1752
1777
  Returns the currently selected position in the active completion
@@ -1755,7 +1780,7 @@ list, or null if no completions are active.
1755
1780
  function selectedCompletionIndex(state) {
1756
1781
  var _a;
1757
1782
  let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
1758
- return open ? open.selected : null;
1783
+ return open && open.selected >= 0 ? open.selected : null;
1759
1784
  }
1760
1785
  /**
1761
1786
  Create an effect that can be attached to a transaction to change
package/dist/index.d.ts CHANGED
@@ -10,6 +10,15 @@ interface CompletionConfig {
10
10
  */
11
11
  activateOnTyping?: boolean;
12
12
  /**
13
+ By default, when completion opens, the first option is selected
14
+ and can be confirmed with
15
+ [`acceptCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.acceptCompletion). When this
16
+ is set to false, the completion widget starts with no completion
17
+ selected, and the user has to explicitly move to a completion
18
+ before you can confirm one.
19
+ */
20
+ selectOnOpen?: boolean;
21
+ /**
13
22
  Override the completion sources used. By default, they will be
14
23
  taken from the `"autocomplete"` [language
15
24
  data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) (which should hold
@@ -63,6 +72,12 @@ interface CompletionConfig {
63
72
  render: (completion: Completion, state: EditorState) => Node | null;
64
73
  position: number;
65
74
  }[];
75
+ /**
76
+ The comparison function to use when sorting completions with the same
77
+ match score. Defaults to using
78
+ [`localeCompare`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
79
+ */
80
+ compareCompletions?: (a: Completion, b: Completion) => number;
66
81
  }
67
82
 
68
83
  /**
package/dist/index.js CHANGED
@@ -229,7 +229,7 @@ class FuzzyMatcher {
229
229
  if (chars.length == 1) {
230
230
  let first = codePointAt(word, 0);
231
231
  return first == chars[0] ? [0, 0, codePointSize(first)]
232
- : first == folded[0] ? [-200 /* CaseFold */, 0, codePointSize(first)] : null;
232
+ : first == folded[0] ? [-200 /* Penalty.CaseFold */, 0, codePointSize(first)] : null;
233
233
  }
234
234
  let direct = word.indexOf(this.pattern);
235
235
  if (direct == 0)
@@ -257,7 +257,7 @@ class FuzzyMatcher {
257
257
  let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
258
258
  let hasLower = /[a-z]/.test(word), wordAdjacent = true;
259
259
  // Go over the option's text, scanning for the various kinds of matches
260
- for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* NonWord */; i < e && byWordTo < len;) {
260
+ for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* Tp.NonWord */; i < e && byWordTo < len;) {
261
261
  let next = codePointAt(word, i);
262
262
  if (direct < 0) {
263
263
  if (preciseTo < len && next == chars[preciseTo])
@@ -275,9 +275,9 @@ class FuzzyMatcher {
275
275
  }
276
276
  }
277
277
  let ch, type = next < 0xff
278
- ? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Lower */ : next >= 65 && next <= 90 ? 1 /* Upper */ : 0 /* NonWord */)
279
- : ((ch = fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Upper */ : ch != ch.toUpperCase() ? 2 /* Lower */ : 0 /* NonWord */);
280
- if (!i || type == 1 /* Upper */ && hasLower || prevType == 0 /* NonWord */ && type != 0 /* NonWord */) {
278
+ ? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Tp.Lower */ : next >= 65 && next <= 90 ? 1 /* Tp.Upper */ : 0 /* Tp.NonWord */)
279
+ : ((ch = fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Tp.Upper */ : ch != ch.toUpperCase() ? 2 /* Tp.Lower */ : 0 /* Tp.NonWord */);
280
+ if (!i || type == 1 /* Tp.Upper */ && hasLower || prevType == 0 /* Tp.NonWord */ && type != 0 /* Tp.NonWord */) {
281
281
  if (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
282
282
  byWord[byWordTo++] = i;
283
283
  else if (byWord.length)
@@ -287,17 +287,17 @@ class FuzzyMatcher {
287
287
  i += codePointSize(next);
288
288
  }
289
289
  if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
290
- return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0), byWord, word);
290
+ return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0), byWord, word);
291
291
  if (adjacentTo == len && adjacentStart == 0)
292
- return [-200 /* CaseFold */ - word.length, 0, adjacentEnd];
292
+ return [-200 /* Penalty.CaseFold */ - word.length, 0, adjacentEnd];
293
293
  if (direct > -1)
294
- return [-700 /* NotStart */ - word.length, direct, direct + this.pattern.length];
294
+ return [-700 /* Penalty.NotStart */ - word.length, direct, direct + this.pattern.length];
295
295
  if (adjacentTo == len)
296
- return [-200 /* CaseFold */ + -700 /* NotStart */ - word.length, adjacentStart, adjacentEnd];
296
+ return [-200 /* Penalty.CaseFold */ + -700 /* Penalty.NotStart */ - word.length, adjacentStart, adjacentEnd];
297
297
  if (byWordTo == len)
298
- return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */ +
299
- (wordAdjacent ? 0 : -1100 /* Gap */), byWord, word);
300
- return chars.length == 2 ? null : this.result((any[0] ? -700 /* NotStart */ : 0) + -200 /* CaseFold */ + -1100 /* Gap */, any, word);
298
+ return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0) + -700 /* Penalty.NotStart */ +
299
+ (wordAdjacent ? 0 : -1100 /* Penalty.Gap */), byWord, word);
300
+ return chars.length == 2 ? null : this.result((any[0] ? -700 /* Penalty.NotStart */ : 0) + -200 /* Penalty.CaseFold */ + -1100 /* Penalty.Gap */, any, word);
301
301
  }
302
302
  result(score, positions, word) {
303
303
  let result = [score - word.length], i = 1;
@@ -318,6 +318,7 @@ const completionConfig = /*@__PURE__*/Facet.define({
318
318
  combine(configs) {
319
319
  return combineConfig(configs, {
320
320
  activateOnTyping: true,
321
+ selectOnOpen: true,
321
322
  override: null,
322
323
  closeOnBlur: true,
323
324
  maxRenderedOptions: 100,
@@ -325,7 +326,8 @@ const completionConfig = /*@__PURE__*/Facet.define({
325
326
  optionClass: () => "",
326
327
  aboveCursor: false,
327
328
  icons: true,
328
- addToOptions: []
329
+ addToOptions: [],
330
+ compareCompletions: (a, b) => a.label.localeCompare(b.label)
329
331
  }, {
330
332
  defaultKeymap: (a, b) => a && b,
331
333
  closeOnBlur: (a, b) => a && b,
@@ -388,6 +390,8 @@ function optionContent(config) {
388
390
  function rangeAroundSelected(total, selected, max) {
389
391
  if (total <= max)
390
392
  return { from: 0, to: total };
393
+ if (selected < 0)
394
+ selected = 0;
391
395
  if (selected <= (total >> 1)) {
392
396
  let off = Math.floor(selected / max);
393
397
  return { from: off * max, to: (off + 1) * max };
@@ -439,7 +443,7 @@ class CompletionTooltip {
439
443
  }
440
444
  updateSel() {
441
445
  let cState = this.view.state.field(this.stateField), open = cState.open;
442
- if (open.selected < this.range.from || open.selected >= this.range.to) {
446
+ if (open.selected > -1 && open.selected < this.range.from || open.selected >= this.range.to) {
443
447
  this.range = rangeAroundSelected(open.options.length, open.selected, this.view.state.facet(completionConfig).maxRenderedOptions);
444
448
  this.list.remove();
445
449
  this.list = this.dom.appendChild(this.createListBox(open.options, cState.id, this.range));
@@ -500,26 +504,47 @@ class CompletionTooltip {
500
504
  let sel = this.dom.querySelector("[aria-selected]");
501
505
  if (!sel || !this.info)
502
506
  return null;
507
+ let win = this.dom.ownerDocument.defaultView;
503
508
  let listRect = this.dom.getBoundingClientRect();
504
509
  let infoRect = this.info.getBoundingClientRect();
505
510
  let selRect = sel.getBoundingClientRect();
506
- if (selRect.top > Math.min(innerHeight, listRect.bottom) - 10 || selRect.bottom < Math.max(0, listRect.top) + 10)
511
+ if (selRect.top > Math.min(win.innerHeight, listRect.bottom) - 10 || selRect.bottom < Math.max(0, listRect.top) + 10)
507
512
  return null;
508
- let top = Math.max(0, Math.min(selRect.top, innerHeight - infoRect.height)) - listRect.top;
509
- let left = this.view.textDirection == Direction.RTL;
510
- let spaceLeft = listRect.left, spaceRight = innerWidth - listRect.right;
513
+ let rtl = this.view.textDirection == Direction.RTL, left = rtl, narrow = false, maxWidth;
514
+ let top = "", bottom = "";
515
+ let spaceLeft = listRect.left, spaceRight = win.innerWidth - listRect.right;
511
516
  if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
512
517
  left = false;
513
518
  else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
514
519
  left = true;
515
- return { top, left };
520
+ if (infoRect.width <= (left ? spaceLeft : spaceRight)) {
521
+ top = (Math.max(0, Math.min(selRect.top, win.innerHeight - infoRect.height)) - listRect.top) + "px";
522
+ maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight) + "px";
523
+ }
524
+ else {
525
+ narrow = true;
526
+ maxWidth = Math.min(400 /* Info.Width */, (rtl ? listRect.right : win.innerWidth - listRect.left) - 30 /* Info.Margin */) + "px";
527
+ let spaceBelow = win.innerHeight - listRect.bottom;
528
+ if (spaceBelow >= infoRect.height || spaceBelow > listRect.top) // Below the completion
529
+ top = (selRect.bottom - listRect.top) + "px";
530
+ else // Above it
531
+ bottom = (listRect.bottom - selRect.top) + "px";
532
+ }
533
+ return {
534
+ top, bottom, maxWidth,
535
+ class: narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right",
536
+ };
516
537
  }
517
538
  positionInfo(pos) {
518
539
  if (this.info) {
519
- this.info.style.top = (pos ? pos.top : -1e6) + "px";
520
540
  if (pos) {
521
- this.info.classList.toggle("cm-completionInfo-left", pos.left);
522
- this.info.classList.toggle("cm-completionInfo-right", !pos.left);
541
+ this.info.style.top = pos.top;
542
+ this.info.style.bottom = pos.bottom;
543
+ this.info.style.maxWidth = pos.maxWidth;
544
+ this.info.className = "cm-tooltip cm-completionInfo cm-completionInfo-" + pos.class;
545
+ }
546
+ else {
547
+ this.info.style.top = "-1e6px";
523
548
  }
524
549
  }
525
550
  }
@@ -595,7 +620,8 @@ function sortOptions(active, state) {
595
620
  }
596
621
  }
597
622
  let result = [], prev = null;
598
- for (let opt of options.sort(cmpOption)) {
623
+ let compare = state.facet(completionConfig).compareCompletions;
624
+ for (let opt of options.sort((a, b) => (b.match[0] - a.match[0]) || compare(a.completion, b.completion))) {
599
625
  if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail ||
600
626
  (prev.type != null && opt.completion.type != null && prev.type != opt.completion.type) ||
601
627
  prev.apply != opt.completion.apply)
@@ -622,8 +648,8 @@ class CompletionDialog {
622
648
  let options = sortOptions(active, state);
623
649
  if (!options.length)
624
650
  return null;
625
- let selected = 0;
626
- if (prev && prev.selected) {
651
+ let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
652
+ if (prev && prev.selected != selected && prev.selected != -1) {
627
653
  let selectedValue = prev.options[prev.selected].completion;
628
654
  for (let i = 0; i < options.length; i++)
629
655
  if (options[i].completion == selectedValue) {
@@ -656,7 +682,7 @@ class CompletionState {
656
682
  state.languageDataAt("autocomplete", cur(state)).map(asSource);
657
683
  let active = sources.map(source => {
658
684
  let value = this.active.find(s => s.source == source) ||
659
- new ActiveSource(source, this.active.some(a => a.state != 0 /* Inactive */) ? 1 /* Pending */ : 0 /* Inactive */);
685
+ new ActiveSource(source, this.active.some(a => a.state != 0 /* State.Inactive */) ? 1 /* State.Pending */ : 0 /* State.Inactive */);
660
686
  return value.update(tr, conf);
661
687
  });
662
688
  if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
@@ -664,8 +690,8 @@ class CompletionState {
664
690
  let open = tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
665
691
  !sameResults(active, this.active) ? CompletionDialog.build(active, state, this.id, this.open, conf)
666
692
  : this.open && tr.docChanged ? this.open.map(tr.changes) : this.open;
667
- if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
668
- active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
693
+ if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult()))
694
+ active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* State.Inactive */) : a);
669
695
  for (let effect of tr.effects)
670
696
  if (effect.is(setSelectedEffect))
671
697
  open = open && open.setSelected(effect.value, this.id);
@@ -693,20 +719,16 @@ const baseAttrs = {
693
719
  "aria-autocomplete": "list"
694
720
  };
695
721
  function makeAttrs(id, selected) {
696
- return {
722
+ let result = {
697
723
  "aria-autocomplete": "list",
698
724
  "aria-haspopup": "listbox",
699
- "aria-activedescendant": id + "-" + selected,
700
725
  "aria-controls": id
701
726
  };
727
+ if (selected > -1)
728
+ result["aria-activedescendant"] = id + "-" + selected;
729
+ return result;
702
730
  }
703
731
  const none = [];
704
- function cmpOption(a, b) {
705
- let dScore = b.match[0] - a.match[0];
706
- if (dScore)
707
- return dScore;
708
- return a.completion.label.localeCompare(b.completion.label);
709
- }
710
732
  function getUserEvent(tr) {
711
733
  return tr.isUserEvent("input.type") ? "input" : tr.isUserEvent("delete.backward") ? "delete" : null;
712
734
  }
@@ -723,13 +745,13 @@ class ActiveSource {
723
745
  value = value.handleUserEvent(tr, event, conf);
724
746
  else if (tr.docChanged)
725
747
  value = value.handleChange(tr);
726
- else if (tr.selection && value.state != 0 /* Inactive */)
727
- value = new ActiveSource(value.source, 0 /* Inactive */);
748
+ else if (tr.selection && value.state != 0 /* State.Inactive */)
749
+ value = new ActiveSource(value.source, 0 /* State.Inactive */);
728
750
  for (let effect of tr.effects) {
729
751
  if (effect.is(startCompletionEffect))
730
- value = new ActiveSource(value.source, 1 /* Pending */, effect.value ? cur(tr.state) : -1);
752
+ value = new ActiveSource(value.source, 1 /* State.Pending */, effect.value ? cur(tr.state) : -1);
731
753
  else if (effect.is(closeCompletionEffect))
732
- value = new ActiveSource(value.source, 0 /* Inactive */);
754
+ value = new ActiveSource(value.source, 0 /* State.Inactive */);
733
755
  else if (effect.is(setActiveEffect))
734
756
  for (let active of effect.value)
735
757
  if (active.source == value.source)
@@ -738,10 +760,10 @@ class ActiveSource {
738
760
  return value;
739
761
  }
740
762
  handleUserEvent(tr, type, conf) {
741
- return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* Pending */);
763
+ return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* State.Pending */);
742
764
  }
743
765
  handleChange(tr) {
744
- return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
766
+ return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
745
767
  }
746
768
  map(changes) {
747
769
  return changes.empty || this.explicitPos < 0 ? this : new ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos));
@@ -749,7 +771,7 @@ class ActiveSource {
749
771
  }
750
772
  class ActiveResult extends ActiveSource {
751
773
  constructor(source, explicitPos, result, from, to) {
752
- super(source, 2 /* Result */, explicitPos);
774
+ super(source, 2 /* State.Result */, explicitPos);
753
775
  this.result = result;
754
776
  this.from = from;
755
777
  this.to = to;
@@ -762,17 +784,17 @@ class ActiveResult extends ActiveSource {
762
784
  if ((this.explicitPos < 0 ? pos <= from : pos < this.from) ||
763
785
  pos > to ||
764
786
  type == "delete" && cur(tr.startState) == this.from)
765
- return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */);
787
+ return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* State.Pending */ : 0 /* State.Inactive */);
766
788
  let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos), updated;
767
789
  if (checkValid(this.result.validFor, tr.state, from, to))
768
790
  return new ActiveResult(this.source, explicitPos, this.result, from, to);
769
791
  if (this.result.update &&
770
792
  (updated = this.result.update(this.result, from, to, new CompletionContext(tr.state, pos, explicitPos >= 0))))
771
793
  return new ActiveResult(this.source, explicitPos, updated, updated.from, (_a = updated.to) !== null && _a !== void 0 ? _a : cur(tr.state));
772
- return new ActiveSource(this.source, 1 /* Pending */, explicitPos);
794
+ return new ActiveSource(this.source, 1 /* State.Pending */, explicitPos);
773
795
  }
774
796
  handleChange(tr) {
775
- return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
797
+ return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
776
798
  }
777
799
  map(mapping) {
778
800
  return mapping.empty ? this :
@@ -814,7 +836,8 @@ function moveCompletionSelection(forward, by = "option") {
814
836
  if (by == "page" && (tooltip = getTooltip(view, cState.open.tooltip)))
815
837
  step = Math.max(2, Math.floor(tooltip.dom.offsetHeight /
816
838
  tooltip.dom.querySelector("li").offsetHeight) - 1);
817
- let selected = cState.open.selected + step * (forward ? 1 : -1), { length } = cState.open.options;
839
+ let { length } = cState.open.options;
840
+ let selected = cState.open.selected > -1 ? cState.open.selected + step * (forward ? 1 : -1) : forward ? 0 : length - 1;
818
841
  if (selected < 0)
819
842
  selected = by == "page" ? 0 : length - 1;
820
843
  else if (selected >= length)
@@ -828,7 +851,8 @@ Accept the current completion.
828
851
  */
829
852
  const acceptCompletion = (view) => {
830
853
  let cState = view.state.field(completionState, false);
831
- if (view.state.readOnly || !cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin)
854
+ if (view.state.readOnly || !cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin ||
855
+ cState.open.selected < 0)
832
856
  return false;
833
857
  applyCompletion(view, cState.open.options[cState.open.selected]);
834
858
  return true;
@@ -848,7 +872,7 @@ Close the currently active completion.
848
872
  */
849
873
  const closeCompletion = (view) => {
850
874
  let cState = view.state.field(completionState, false);
851
- if (!cState || !cState.active.some(a => a.state != 0 /* Inactive */))
875
+ if (!cState || !cState.active.some(a => a.state != 0 /* State.Inactive */))
852
876
  return false;
853
877
  view.dispatch({ effects: closeCompletionEffect.of(null) });
854
878
  return true;
@@ -871,9 +895,9 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
871
895
  this.debounceUpdate = -1;
872
896
  this.running = [];
873
897
  this.debounceAccept = -1;
874
- this.composing = 0 /* None */;
898
+ this.composing = 0 /* CompositionState.None */;
875
899
  for (let active of view.state.field(completionState).active)
876
- if (active.state == 1 /* Pending */)
900
+ if (active.state == 1 /* State.Pending */)
877
901
  this.startQuery(active);
878
902
  }
879
903
  update(update) {
@@ -904,21 +928,21 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
904
928
  }
905
929
  if (this.debounceUpdate > -1)
906
930
  clearTimeout(this.debounceUpdate);
907
- this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.active.source == a.source))
931
+ this.debounceUpdate = cState.active.some(a => a.state == 1 /* State.Pending */ && !this.running.some(q => q.active.source == a.source))
908
932
  ? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
909
- if (this.composing != 0 /* None */)
933
+ if (this.composing != 0 /* CompositionState.None */)
910
934
  for (let tr of update.transactions) {
911
935
  if (getUserEvent(tr) == "input")
912
- this.composing = 2 /* Changed */;
913
- else if (this.composing == 2 /* Changed */ && tr.selection)
914
- this.composing = 3 /* ChangedAndMoved */;
936
+ this.composing = 2 /* CompositionState.Changed */;
937
+ else if (this.composing == 2 /* CompositionState.Changed */ && tr.selection)
938
+ this.composing = 3 /* CompositionState.ChangedAndMoved */;
915
939
  }
916
940
  }
917
941
  startUpdate() {
918
942
  this.debounceUpdate = -1;
919
943
  let { state } = this.view, cState = state.field(completionState);
920
944
  for (let active of cState.active) {
921
- if (active.state == 1 /* Pending */ && !this.running.some(r => r.active.source == active.source))
945
+ if (active.state == 1 /* State.Pending */ && !this.running.some(r => r.active.source == active.source))
922
946
  this.startQuery(active);
923
947
  }
924
948
  }
@@ -969,14 +993,14 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
969
993
  }
970
994
  }
971
995
  let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
972
- if (current && current.state == 1 /* Pending */) {
996
+ if (current && current.state == 1 /* State.Pending */) {
973
997
  if (query.done == null) {
974
998
  // Explicitly failed. Should clear the pending status if it
975
999
  // hasn't been re-set in the meantime.
976
- let active = new ActiveSource(query.active.source, 0 /* Inactive */);
1000
+ let active = new ActiveSource(query.active.source, 0 /* State.Inactive */);
977
1001
  for (let tr of query.updates)
978
1002
  active = active.update(tr, conf);
979
- if (active.state != 1 /* Pending */)
1003
+ if (active.state != 1 /* State.Pending */)
980
1004
  updated.push(active);
981
1005
  }
982
1006
  else {
@@ -996,15 +1020,15 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
996
1020
  this.view.dispatch({ effects: closeCompletionEffect.of(null) });
997
1021
  },
998
1022
  compositionstart() {
999
- this.composing = 1 /* Started */;
1023
+ this.composing = 1 /* CompositionState.Started */;
1000
1024
  },
1001
1025
  compositionend() {
1002
- if (this.composing == 3 /* ChangedAndMoved */) {
1026
+ if (this.composing == 3 /* CompositionState.ChangedAndMoved */) {
1003
1027
  // Safari fires compositionend events synchronously, possibly
1004
1028
  // from inside an update, so dispatch asynchronously to avoid reentrancy
1005
1029
  setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
1006
1030
  }
1007
- this.composing = 0 /* None */;
1031
+ this.composing = 0 /* CompositionState.None */;
1008
1032
  }
1009
1033
  }
1010
1034
  });
@@ -1049,10 +1073,13 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
1049
1073
  position: "absolute",
1050
1074
  padding: "3px 9px",
1051
1075
  width: "max-content",
1052
- maxWidth: "300px",
1076
+ maxWidth: `${400 /* Info.Width */}px`,
1077
+ boxSizing: "border-box"
1053
1078
  },
1054
1079
  ".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
1055
1080
  ".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
1081
+ ".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Info.Margin */}px` },
1082
+ ".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Info.Margin */}px` },
1056
1083
  "&light .cm-snippetField": { backgroundColor: "#00000022" },
1057
1084
  "&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
1058
1085
  ".cm-snippetFieldPosition": {
@@ -1387,7 +1414,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
1387
1414
  if (!seen[m[0]] && pos + m.index != ignoreAt) {
1388
1415
  result.push({ type: "text", label: m[0] });
1389
1416
  seen[m[0]] = true;
1390
- if (result.length >= 2000 /* MaxList */)
1417
+ if (result.length >= 2000 /* C.MaxList */)
1391
1418
  return;
1392
1419
  }
1393
1420
  }
@@ -1395,7 +1422,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
1395
1422
  }
1396
1423
  }
1397
1424
  function collectWords(doc, cache, wordRE, to, ignoreAt) {
1398
- let big = doc.length >= 1000 /* MinCacheLen */;
1425
+ let big = doc.length >= 1000 /* C.MinCacheLen */;
1399
1426
  let cached = big && cache.get(doc);
1400
1427
  if (cached)
1401
1428
  return cached;
@@ -1403,7 +1430,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
1403
1430
  if (doc.children) {
1404
1431
  let pos = 0;
1405
1432
  for (let ch of doc.children) {
1406
- if (ch.length >= 1000 /* MinCacheLen */) {
1433
+ if (ch.length >= 1000 /* C.MinCacheLen */) {
1407
1434
  for (let c of collectWords(ch, cache, wordRE, to - pos, ignoreAt - pos)) {
1408
1435
  if (!seen[c.label]) {
1409
1436
  seen[c.label] = true;
@@ -1420,7 +1447,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
1420
1447
  else {
1421
1448
  storeWords(doc, wordRE, result, seen, ignoreAt);
1422
1449
  }
1423
- if (big && result.length < 2000 /* MaxList */)
1450
+ if (big && result.length < 2000 /* C.MaxList */)
1424
1451
  cache.set(doc, result);
1425
1452
  return result;
1426
1453
  }
@@ -1436,7 +1463,7 @@ const completeAnyWord = context => {
1436
1463
  if (!token && !context.explicit)
1437
1464
  return null;
1438
1465
  let from = token ? token.from : context.pos;
1439
- let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* Range */, from);
1466
+ let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* C.Range */, from);
1440
1467
  return { from, options, validFor: mapRE(re, s => "^" + s) };
1441
1468
  };
1442
1469
 
@@ -1525,14 +1552,13 @@ const deleteBracketPair = ({ state, dispatch }) => {
1525
1552
  for (let token of tokens) {
1526
1553
  if (token == before && nextChar(state.doc, range.head) == closing(codePointAt(token, 0)))
1527
1554
  return { changes: { from: range.head - token.length, to: range.head + token.length },
1528
- range: EditorSelection.cursor(range.head - token.length),
1529
- userEvent: "delete.backward" };
1555
+ range: EditorSelection.cursor(range.head - token.length) };
1530
1556
  }
1531
1557
  }
1532
1558
  return { range: dont = range };
1533
1559
  });
1534
1560
  if (!dont)
1535
- dispatch(state.update(changes, { scrollIntoView: true }));
1561
+ dispatch(state.update(changes, { scrollIntoView: true, userEvent: "delete.backward" }));
1536
1562
  return !dont;
1537
1563
  };
1538
1564
  /**
@@ -1658,7 +1684,6 @@ function nodeStart(state, pos) {
1658
1684
  return tree.parent && tree.from == pos;
1659
1685
  }
1660
1686
  function probablyInString(state, pos, quoteToken) {
1661
- console.log('check pos', pos);
1662
1687
  let node = syntaxTree(state).resolveInner(pos, -1);
1663
1688
  for (let i = 0; i < 5; i++) {
1664
1689
  if (state.sliceDoc(node.from, node.from + quoteToken.length) == quoteToken) {
@@ -1719,8 +1744,8 @@ returns `null`.
1719
1744
  */
1720
1745
  function completionStatus(state) {
1721
1746
  let cState = state.field(completionState, false);
1722
- return cState && cState.active.some(a => a.state == 1 /* Pending */) ? "pending"
1723
- : cState && cState.active.some(a => a.state != 0 /* Inactive */) ? "active" : null;
1747
+ return cState && cState.active.some(a => a.state == 1 /* State.Pending */) ? "pending"
1748
+ : cState && cState.active.some(a => a.state != 0 /* State.Inactive */) ? "active" : null;
1724
1749
  }
1725
1750
  const completionArrayCache = /*@__PURE__*/new WeakMap;
1726
1751
  /**
@@ -1742,7 +1767,7 @@ Return the currently selected completion, if any.
1742
1767
  function selectedCompletion(state) {
1743
1768
  var _a;
1744
1769
  let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
1745
- return open ? open.options[open.selected].completion : null;
1770
+ return open && open.selected >= 0 ? open.options[open.selected].completion : null;
1746
1771
  }
1747
1772
  /**
1748
1773
  Returns the currently selected position in the active completion
@@ -1751,7 +1776,7 @@ list, or null if no completions are active.
1751
1776
  function selectedCompletionIndex(state) {
1752
1777
  var _a;
1753
1778
  let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
1754
- return open ? open.selected : null;
1779
+ return open && open.selected >= 0 ? open.selected : null;
1755
1780
  }
1756
1781
  /**
1757
1782
  Create an effect that can be attached to a transaction to change
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/autocomplete",
3
- "version": "6.0.3",
3
+ "version": "6.1.1",
4
4
  "description": "Autocompletion for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",