@codemirror/autocomplete 6.8.1 → 6.9.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,17 @@
1
+ ## 6.9.1 (2023-09-14)
2
+
3
+ ### Bug fixes
4
+
5
+ Make sure the cursor is scrolled into view after inserting completion text.
6
+
7
+ Make sure scrolling completions into view doesn't get confused when the tooltip is scaled.
8
+
9
+ ## 6.9.0 (2023-07-18)
10
+
11
+ ### New features
12
+
13
+ Completions may now provide a `displayLabel` property that overrides the way they are displayed in the completion list.
14
+
1
15
  ## 6.8.1 (2023-06-23)
2
16
 
3
17
  ### Bug fixes
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) {
@@ -202,6 +200,8 @@ class FuzzyMatcher {
202
200
  this.any = [];
203
201
  this.precise = [];
204
202
  this.byWord = [];
203
+ this.score = 0;
204
+ this.matched = [];
205
205
  for (let p = 0; p < pattern.length;) {
206
206
  let char = state.codePointAt(pattern, p), size = state.codePointSize(char);
207
207
  this.chars.push(char);
@@ -211,34 +211,39 @@ class FuzzyMatcher {
211
211
  }
212
212
  this.astral = pattern.length != this.chars.length;
213
213
  }
214
+ ret(score, matched) {
215
+ this.score = score;
216
+ this.matched = matched;
217
+ return true;
218
+ }
214
219
  // Matches a given word (completion) against the pattern (input).
215
- // Will return null for no match, and otherwise an array that starts
216
- // with the match score, followed by any number of `from, to` pairs
217
- // indicating the matched parts of `word`.
220
+ // Will return a boolean indicating whether there was a match and,
221
+ // on success, set `this.score` to the score, `this.matched` to an
222
+ // array of `from, to` pairs indicating the matched parts of `word`.
218
223
  //
219
224
  // The score is a number that is more negative the worse the match
220
225
  // is. See `Penalty` above.
221
226
  match(word) {
222
227
  if (this.pattern.length == 0)
223
- return [-100 /* NotFull */];
228
+ return this.ret(-100 /* Penalty.NotFull */, []);
224
229
  if (word.length < this.pattern.length)
225
- return null;
230
+ return false;
226
231
  let { chars, folded, any, precise, byWord } = this;
227
232
  // For single-character queries, only match when they occur right
228
233
  // at the start
229
234
  if (chars.length == 1) {
230
235
  let first = state.codePointAt(word, 0), firstSize = state.codePointSize(first);
231
- let score = firstSize == word.length ? 0 : -100 /* NotFull */;
236
+ let score = firstSize == word.length ? 0 : -100 /* Penalty.NotFull */;
232
237
  if (first == chars[0]) ;
233
238
  else if (first == folded[0])
234
- score += -200 /* CaseFold */;
239
+ score += -200 /* Penalty.CaseFold */;
235
240
  else
236
- return null;
237
- return [score, 0, firstSize];
241
+ return false;
242
+ return this.ret(score, [0, firstSize]);
238
243
  }
239
244
  let direct = word.indexOf(this.pattern);
240
245
  if (direct == 0)
241
- return [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]);
242
247
  let len = chars.length, anyTo = 0;
243
248
  if (direct < 0) {
244
249
  for (let i = 0, e = Math.min(word.length, 200); i < e && anyTo < len;) {
@@ -249,7 +254,7 @@ class FuzzyMatcher {
249
254
  }
250
255
  // No match, exit immediately
251
256
  if (anyTo < len)
252
- return null;
257
+ return false;
253
258
  }
254
259
  // This tracks the extent of the precise (non-folded, not
255
260
  // necessarily adjacent) match
@@ -262,7 +267,7 @@ class FuzzyMatcher {
262
267
  let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
263
268
  let hasLower = /[a-z]/.test(word), wordAdjacent = true;
264
269
  // Go over the option's text, scanning for the various kinds of matches
265
- 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;) {
266
271
  let next = state.codePointAt(word, i);
267
272
  if (direct < 0) {
268
273
  if (preciseTo < len && next == chars[preciseTo])
@@ -280,9 +285,9 @@ class FuzzyMatcher {
280
285
  }
281
286
  }
282
287
  let ch, type = next < 0xff
283
- ? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Lower */ : next >= 65 && next <= 90 ? 1 /* Upper */ : 0 /* NonWord */)
284
- : ((ch = state.fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Upper */ : ch != ch.toUpperCase() ? 2 /* Lower */ : 0 /* NonWord */);
285
- 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 */) {
286
291
  if (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
287
292
  byWord[byWordTo++] = i;
288
293
  else if (byWord.length)
@@ -292,30 +297,31 @@ class FuzzyMatcher {
292
297
  i += state.codePointSize(next);
293
298
  }
294
299
  if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
295
- 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);
296
301
  if (adjacentTo == len && adjacentStart == 0)
297
- return [-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]);
298
303
  if (direct > -1)
299
- return [-700 /* NotStart */ - word.length, direct, direct + this.pattern.length];
304
+ return this.ret(-700 /* Penalty.NotStart */ - word.length, [direct, direct + this.pattern.length]);
300
305
  if (adjacentTo == len)
301
- return [-200 /* CaseFold */ + -700 /* NotStart */ - word.length, adjacentStart, adjacentEnd];
306
+ return this.ret(-200 /* Penalty.CaseFold */ + -700 /* Penalty.NotStart */ - word.length, [adjacentStart, adjacentEnd]);
302
307
  if (byWordTo == len)
303
- return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */ +
304
- (wordAdjacent ? 0 : -1100 /* Gap */), byWord, word);
305
- return chars.length == 2 ? null : this.result((any[0] ? -700 /* NotStart */ : 0) + -200 /* CaseFold */ + -1100 /* Gap */, any, word);
308
+ return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0) + -700 /* Penalty.NotStart */ +
309
+ (wordAdjacent ? 0 : -1100 /* Penalty.Gap */), byWord, word);
310
+ return chars.length == 2 ? false
311
+ : this.result((any[0] ? -700 /* Penalty.NotStart */ : 0) + -200 /* Penalty.CaseFold */ + -1100 /* Penalty.Gap */, any, word);
306
312
  }
307
313
  result(score, positions, word) {
308
- let result = [score - word.length], i = 1;
314
+ let result = [], i = 0;
309
315
  for (let pos of positions) {
310
316
  let to = pos + (this.astral ? state.codePointSize(state.codePointAt(word, pos)) : 1);
311
- if (i > 1 && result[i - 1] == pos)
317
+ if (i && result[i - 1] == pos)
312
318
  result[i - 1] = to;
313
319
  else {
314
320
  result[i++] = pos;
315
321
  result[i++] = to;
316
322
  }
317
323
  }
318
- return result;
324
+ return this.ret(score - word.length, result);
319
325
  }
320
326
  }
321
327
 
@@ -349,7 +355,7 @@ const completionConfig = state.Facet.define({
349
355
  function joinClass(a, b) {
350
356
  return a ? b ? a + " " + b : a : b;
351
357
  }
352
- function defaultPositionInfo(view$1, list, option, info, space) {
358
+ function defaultPositionInfo(view$1, list, option, info, space, tooltip) {
353
359
  let rtl = view$1.textDirection == view.Direction.RTL, left = rtl, narrow = false;
354
360
  let side = "top", offset, maxWidth;
355
361
  let spaceLeft = list.left - space.left, spaceRight = space.right - list.right;
@@ -360,11 +366,11 @@ function defaultPositionInfo(view$1, list, option, info, space) {
360
366
  left = true;
361
367
  if (infoWidth <= (left ? spaceLeft : spaceRight)) {
362
368
  offset = Math.max(space.top, Math.min(option.top, space.bottom - infoHeight)) - list.top;
363
- maxWidth = Math.min(400 /* Width */, left ? spaceLeft : spaceRight);
369
+ maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight);
364
370
  }
365
371
  else {
366
372
  narrow = true;
367
- 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 */);
368
374
  let spaceBelow = space.bottom - list.bottom;
369
375
  if (spaceBelow >= infoHeight || spaceBelow > list.top) { // Below the completion
370
376
  offset = option.bottom - list.top;
@@ -374,8 +380,10 @@ function defaultPositionInfo(view$1, list, option, info, space) {
374
380
  offset = list.bottom - option.top;
375
381
  }
376
382
  }
383
+ let scaleY = (list.bottom - list.top) / tooltip.offsetHeight;
384
+ let scaleX = (list.right - list.left) / tooltip.offsetWidth;
377
385
  return {
378
- style: `${side}: ${offset}px; max-width: ${maxWidth}px`,
386
+ style: `${side}: ${offset / scaleY}px; max-width: ${maxWidth / scaleX}px`,
379
387
  class: "cm-completionInfo-" + (narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right")
380
388
  };
381
389
  }
@@ -398,8 +406,8 @@ function optionContent(config) {
398
406
  render(completion, _s, match) {
399
407
  let labelElt = document.createElement("span");
400
408
  labelElt.className = "cm-completionLabel";
401
- let { label } = completion, off = 0;
402
- for (let j = 1; j < match.length;) {
409
+ let label = completion.displayLabel || completion.label, off = 0;
410
+ for (let j = 0; j < match.length;) {
403
411
  let from = match[j++], to = match[j++];
404
412
  if (from > off)
405
413
  labelElt.appendChild(document.createTextNode(label.slice(off, from)));
@@ -595,7 +603,7 @@ class CompletionTooltip {
595
603
  if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 ||
596
604
  selRect.bottom < Math.max(space.top, listRect.top) + 10)
597
605
  return null;
598
- return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space);
606
+ return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space, this.dom);
599
607
  }
600
608
  placeInfo(pos) {
601
609
  if (this.info) {
@@ -669,10 +677,11 @@ function completionTooltip(stateField, applyCompletion) {
669
677
  function scrollIntoView(container, element) {
670
678
  let parent = container.getBoundingClientRect();
671
679
  let self = element.getBoundingClientRect();
680
+ let scaleY = parent.height / container.offsetHeight;
672
681
  if (self.top < parent.top)
673
- container.scrollTop -= parent.top - self.top;
682
+ container.scrollTop -= (parent.top - self.top) / scaleY;
674
683
  else if (self.bottom > parent.bottom)
675
- container.scrollTop += self.bottom - parent.bottom;
684
+ container.scrollTop += (self.bottom - parent.bottom) / scaleY;
676
685
  }
677
686
 
678
687
  // Used to pick a preferred option when two options with the same
@@ -697,21 +706,18 @@ function sortOptions(active, state) {
697
706
  };
698
707
  for (let a of active)
699
708
  if (a.hasResult()) {
709
+ let getMatch = a.result.getMatch;
700
710
  if (a.result.filter === false) {
701
- let getMatch = a.result.getMatch;
702
711
  for (let option of a.result.options) {
703
- let match = [1e9 - options.length];
704
- if (getMatch)
705
- for (let n of getMatch(option))
706
- match.push(n);
707
- addOption(new Option(option, a.source, match, match[0]));
712
+ addOption(new Option(option, a.source, getMatch ? getMatch(option) : [], 1e9 - options.length));
708
713
  }
709
714
  }
710
715
  else {
711
- let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match;
716
+ let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to));
712
717
  for (let option of a.result.options)
713
- if (match = matcher.match(option.label)) {
714
- addOption(new Option(option, a.source, match, match[0] + (option.boost || 0)));
718
+ if (matcher.match(option.label)) {
719
+ let matched = !option.displayLabel ? matcher.matched : getMatch ? getMatch(option, matcher.matched) : [];
720
+ addOption(new Option(option, a.source, matched, matcher.score + (option.boost || 0)));
715
721
  }
716
722
  }
717
723
  }
@@ -758,7 +764,7 @@ class CompletionDialog {
758
764
  static build(active, state, id, prev, conf) {
759
765
  let options = sortOptions(active, state);
760
766
  if (!options.length) {
761
- return prev && active.some(a => a.state == 1 /* Pending */) ?
767
+ return prev && active.some(a => a.state == 1 /* State.Pending */) ?
762
768
  new CompletionDialog(prev.options, prev.attrs, prev.tooltip, prev.timestamp, prev.selected, true) : null;
763
769
  }
764
770
  let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
@@ -795,7 +801,7 @@ class CompletionState {
795
801
  state.languageDataAt("autocomplete", cur(state)).map(asSource);
796
802
  let active = sources.map(source => {
797
803
  let value = this.active.find(s => s.source == source) ||
798
- new ActiveSource(source, this.active.some(a => a.state != 0 /* Inactive */) ? 1 /* Pending */ : 0 /* Inactive */);
804
+ new ActiveSource(source, this.active.some(a => a.state != 0 /* State.Inactive */) ? 1 /* State.Pending */ : 0 /* State.Inactive */);
799
805
  return value.update(tr, conf);
800
806
  });
801
807
  if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
@@ -806,10 +812,10 @@ class CompletionState {
806
812
  if (tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
807
813
  !sameResults(active, this.active))
808
814
  open = CompletionDialog.build(active, state, this.id, open, conf);
809
- else if (open && open.disabled && !active.some(a => a.state == 1 /* Pending */))
815
+ else if (open && open.disabled && !active.some(a => a.state == 1 /* State.Pending */))
810
816
  open = null;
811
- if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
812
- active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
817
+ if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult()))
818
+ active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* State.Inactive */) : a);
813
819
  for (let effect of tr.effects)
814
820
  if (effect.is(setSelectedEffect))
815
821
  open = open && open.setSelected(effect.value, this.id);
@@ -863,13 +869,13 @@ class ActiveSource {
863
869
  value = value.handleUserEvent(tr, event, conf);
864
870
  else if (tr.docChanged)
865
871
  value = value.handleChange(tr);
866
- else if (tr.selection && value.state != 0 /* Inactive */)
867
- value = new ActiveSource(value.source, 0 /* Inactive */);
872
+ else if (tr.selection && value.state != 0 /* State.Inactive */)
873
+ value = new ActiveSource(value.source, 0 /* State.Inactive */);
868
874
  for (let effect of tr.effects) {
869
875
  if (effect.is(startCompletionEffect))
870
- value = new ActiveSource(value.source, 1 /* Pending */, effect.value ? cur(tr.state) : -1);
876
+ value = new ActiveSource(value.source, 1 /* State.Pending */, effect.value ? cur(tr.state) : -1);
871
877
  else if (effect.is(closeCompletionEffect))
872
- value = new ActiveSource(value.source, 0 /* Inactive */);
878
+ value = new ActiveSource(value.source, 0 /* State.Inactive */);
873
879
  else if (effect.is(setActiveEffect))
874
880
  for (let active of effect.value)
875
881
  if (active.source == value.source)
@@ -878,10 +884,10 @@ class ActiveSource {
878
884
  return value;
879
885
  }
880
886
  handleUserEvent(tr, type, conf) {
881
- return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* Pending */);
887
+ return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* State.Pending */);
882
888
  }
883
889
  handleChange(tr) {
884
- return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
890
+ return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
885
891
  }
886
892
  map(changes) {
887
893
  return changes.empty || this.explicitPos < 0 ? this : new ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos));
@@ -889,7 +895,7 @@ class ActiveSource {
889
895
  }
890
896
  class ActiveResult extends ActiveSource {
891
897
  constructor(source, explicitPos, result, from, to) {
892
- super(source, 2 /* Result */, explicitPos);
898
+ super(source, 2 /* State.Result */, explicitPos);
893
899
  this.result = result;
894
900
  this.from = from;
895
901
  this.to = to;
@@ -902,17 +908,17 @@ class ActiveResult extends ActiveSource {
902
908
  if ((this.explicitPos < 0 ? pos <= from : pos < this.from) ||
903
909
  pos > to ||
904
910
  type == "delete" && cur(tr.startState) == this.from)
905
- return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */);
911
+ return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* State.Pending */ : 0 /* State.Inactive */);
906
912
  let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos), updated;
907
913
  if (checkValid(this.result.validFor, tr.state, from, to))
908
914
  return new ActiveResult(this.source, explicitPos, this.result, from, to);
909
915
  if (this.result.update &&
910
916
  (updated = this.result.update(this.result, from, to, new CompletionContext(tr.state, pos, explicitPos >= 0))))
911
917
  return new ActiveResult(this.source, explicitPos, updated, updated.from, (_a = updated.to) !== null && _a !== void 0 ? _a : cur(tr.state));
912
- return new ActiveSource(this.source, 1 /* Pending */, explicitPos);
918
+ return new ActiveSource(this.source, 1 /* State.Pending */, explicitPos);
913
919
  }
914
920
  handleChange(tr) {
915
- return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
921
+ return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
916
922
  }
917
923
  map(mapping) {
918
924
  return mapping.empty ? this :
@@ -998,7 +1004,7 @@ Close the currently active completion.
998
1004
  */
999
1005
  const closeCompletion = (view) => {
1000
1006
  let cState = view.state.field(completionState, false);
1001
- if (!cState || !cState.active.some(a => a.state != 0 /* Inactive */))
1007
+ if (!cState || !cState.active.some(a => a.state != 0 /* State.Inactive */))
1002
1008
  return false;
1003
1009
  view.dispatch({ effects: closeCompletionEffect.of(null) });
1004
1010
  return true;
@@ -1021,9 +1027,9 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
1021
1027
  this.debounceUpdate = -1;
1022
1028
  this.running = [];
1023
1029
  this.debounceAccept = -1;
1024
- this.composing = 0 /* None */;
1030
+ this.composing = 0 /* CompositionState.None */;
1025
1031
  for (let active of view.state.field(completionState).active)
1026
- if (active.state == 1 /* Pending */)
1032
+ if (active.state == 1 /* State.Pending */)
1027
1033
  this.startQuery(active);
1028
1034
  }
1029
1035
  update(update) {
@@ -1054,21 +1060,21 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
1054
1060
  }
1055
1061
  if (this.debounceUpdate > -1)
1056
1062
  clearTimeout(this.debounceUpdate);
1057
- this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.active.source == a.source))
1063
+ this.debounceUpdate = cState.active.some(a => a.state == 1 /* State.Pending */ && !this.running.some(q => q.active.source == a.source))
1058
1064
  ? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
1059
- if (this.composing != 0 /* None */)
1065
+ if (this.composing != 0 /* CompositionState.None */)
1060
1066
  for (let tr of update.transactions) {
1061
1067
  if (getUserEvent(tr) == "input")
1062
- this.composing = 2 /* Changed */;
1063
- else if (this.composing == 2 /* Changed */ && tr.selection)
1064
- this.composing = 3 /* ChangedAndMoved */;
1068
+ this.composing = 2 /* CompositionState.Changed */;
1069
+ else if (this.composing == 2 /* CompositionState.Changed */ && tr.selection)
1070
+ this.composing = 3 /* CompositionState.ChangedAndMoved */;
1065
1071
  }
1066
1072
  }
1067
1073
  startUpdate() {
1068
1074
  this.debounceUpdate = -1;
1069
1075
  let { state } = this.view, cState = state.field(completionState);
1070
1076
  for (let active of cState.active) {
1071
- if (active.state == 1 /* Pending */ && !this.running.some(r => r.active.source == active.source))
1077
+ if (active.state == 1 /* State.Pending */ && !this.running.some(r => r.active.source == active.source))
1072
1078
  this.startQuery(active);
1073
1079
  }
1074
1080
  }
@@ -1119,14 +1125,14 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
1119
1125
  }
1120
1126
  }
1121
1127
  let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
1122
- if (current && current.state == 1 /* Pending */) {
1128
+ if (current && current.state == 1 /* State.Pending */) {
1123
1129
  if (query.done == null) {
1124
1130
  // Explicitly failed. Should clear the pending status if it
1125
1131
  // hasn't been re-set in the meantime.
1126
- let active = new ActiveSource(query.active.source, 0 /* Inactive */);
1132
+ let active = new ActiveSource(query.active.source, 0 /* State.Inactive */);
1127
1133
  for (let tr of query.updates)
1128
1134
  active = active.update(tr, conf);
1129
- if (active.state != 1 /* Pending */)
1135
+ if (active.state != 1 /* State.Pending */)
1130
1136
  updated.push(active);
1131
1137
  }
1132
1138
  else {
@@ -1149,15 +1155,15 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
1149
1155
  }
1150
1156
  },
1151
1157
  compositionstart() {
1152
- this.composing = 1 /* Started */;
1158
+ this.composing = 1 /* CompositionState.Started */;
1153
1159
  },
1154
1160
  compositionend() {
1155
- if (this.composing == 3 /* ChangedAndMoved */) {
1161
+ if (this.composing == 3 /* CompositionState.ChangedAndMoved */) {
1156
1162
  // Safari fires compositionend events synchronously, possibly
1157
1163
  // from inside an update, so dispatch asynchronously to avoid reentrancy
1158
1164
  setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
1159
1165
  }
1160
- this.composing = 0 /* None */;
1166
+ this.composing = 0 /* CompositionState.None */;
1161
1167
  }
1162
1168
  }
1163
1169
  });
@@ -1217,13 +1223,13 @@ const baseTheme = view.EditorView.baseTheme({
1217
1223
  position: "absolute",
1218
1224
  padding: "3px 9px",
1219
1225
  width: "max-content",
1220
- maxWidth: `${400 /* Width */}px`,
1226
+ maxWidth: `${400 /* Info.Width */}px`,
1221
1227
  boxSizing: "border-box"
1222
1228
  },
1223
1229
  ".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
1224
1230
  ".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
1225
- ".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Margin */}px` },
1226
- ".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Margin */}px` },
1231
+ ".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Info.Margin */}px` },
1232
+ ".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Info.Margin */}px` },
1227
1233
  "&light .cm-snippetField": { backgroundColor: "#00000022" },
1228
1234
  "&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
1229
1235
  ".cm-snippetFieldPosition": {
@@ -1474,7 +1480,8 @@ function moveField(dir) {
1474
1480
  let next = active.active + dir, last = dir > 0 && !active.ranges.some(r => r.field == next + dir);
1475
1481
  dispatch(state.update({
1476
1482
  selection: fieldSelection(active.ranges, next),
1477
- effects: setActive.of(last ? null : new ActiveSnippet(active.ranges, next))
1483
+ effects: setActive.of(last ? null : new ActiveSnippet(active.ranges, next)),
1484
+ scrollIntoView: true
1478
1485
  }));
1479
1486
  return true;
1480
1487
  };
@@ -1546,7 +1553,9 @@ const snippetPointerHandler = view.EditorView.domEventHandlers({
1546
1553
  return false;
1547
1554
  view.dispatch({
1548
1555
  selection: fieldSelection(active.ranges, match.field),
1549
- effects: setActive.of(active.ranges.some(r => r.field > match.field) ? new ActiveSnippet(active.ranges, match.field) : null)
1556
+ effects: setActive.of(active.ranges.some(r => r.field > match.field)
1557
+ ? new ActiveSnippet(active.ranges, match.field) : null),
1558
+ scrollIntoView: true
1550
1559
  });
1551
1560
  return true;
1552
1561
  }
@@ -1576,7 +1585,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
1576
1585
  if (!seen[m[0]] && pos + m.index != ignoreAt) {
1577
1586
  result.push({ type: "text", label: m[0] });
1578
1587
  seen[m[0]] = true;
1579
- if (result.length >= 2000 /* MaxList */)
1588
+ if (result.length >= 2000 /* C.MaxList */)
1580
1589
  return;
1581
1590
  }
1582
1591
  }
@@ -1584,7 +1593,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
1584
1593
  }
1585
1594
  }
1586
1595
  function collectWords(doc, cache, wordRE, to, ignoreAt) {
1587
- let big = doc.length >= 1000 /* MinCacheLen */;
1596
+ let big = doc.length >= 1000 /* C.MinCacheLen */;
1588
1597
  let cached = big && cache.get(doc);
1589
1598
  if (cached)
1590
1599
  return cached;
@@ -1592,7 +1601,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
1592
1601
  if (doc.children) {
1593
1602
  let pos = 0;
1594
1603
  for (let ch of doc.children) {
1595
- if (ch.length >= 1000 /* MinCacheLen */) {
1604
+ if (ch.length >= 1000 /* C.MinCacheLen */) {
1596
1605
  for (let c of collectWords(ch, cache, wordRE, to - pos, ignoreAt - pos)) {
1597
1606
  if (!seen[c.label]) {
1598
1607
  seen[c.label] = true;
@@ -1609,7 +1618,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
1609
1618
  else {
1610
1619
  storeWords(doc, wordRE, result, seen, ignoreAt);
1611
1620
  }
1612
- if (big && result.length < 2000 /* MaxList */)
1621
+ if (big && result.length < 2000 /* C.MaxList */)
1613
1622
  cache.set(doc, result);
1614
1623
  return result;
1615
1624
  }
@@ -1625,7 +1634,7 @@ const completeAnyWord = context => {
1625
1634
  if (!token && !context.explicit)
1626
1635
  return null;
1627
1636
  let from = token ? token.from : context.pos;
1628
- let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* Range */, from);
1637
+ let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* C.Range */, from);
1629
1638
  return { from, options, validFor: mapRE(re, s => "^" + s) };
1630
1639
  };
1631
1640
 
@@ -1917,8 +1926,8 @@ returns `null`.
1917
1926
  */
1918
1927
  function completionStatus(state) {
1919
1928
  let cState = state.field(completionState, false);
1920
- return cState && cState.active.some(a => a.state == 1 /* Pending */) ? "pending"
1921
- : cState && cState.active.some(a => a.state != 0 /* Inactive */) ? "active" : null;
1929
+ return cState && cState.active.some(a => a.state == 1 /* State.Pending */) ? "pending"
1930
+ : cState && cState.active.some(a => a.state != 0 /* State.Inactive */) ? "active" : null;
1922
1931
  }
1923
1932
  const completionArrayCache = new WeakMap;
1924
1933
  /**
package/dist/index.d.cts CHANGED
@@ -9,11 +9,18 @@ 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;
16
16
  /**
17
+ An optional override for the completion's visible label. When
18
+ using this, matched characters will only be highlighted if you
19
+ provide a [`getMatch`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.getMatch)
20
+ function.
21
+ */
22
+ displayLabel?: string;
23
+ /**
17
24
  An optional short piece of information to show (with a different
18
25
  style) after the label.
19
26
  */
@@ -68,7 +75,7 @@ The type returned from
68
75
  node, null to indicate there is no info, or an object with an
69
76
  optional `destroy` method that cleans up the node.
70
77
  */
71
- declare type CompletionInfo = Node | null | {
78
+ type CompletionInfo = Node | null | {
72
79
  dom: Node;
73
80
  destroy?(): void;
74
81
  };
@@ -189,7 +196,7 @@ may return its [result](https://codemirror.net/6/docs/ref/#autocomplete.Completi
189
196
  synchronously or as a promise. Returning null indicates no
190
197
  completions are available.
191
198
  */
192
- declare type CompletionSource = (context: CompletionContext) => CompletionResult | null | Promise<CompletionResult | null>;
199
+ type CompletionSource = (context: CompletionContext) => CompletionResult | null | Promise<CompletionResult | null>;
193
200
  /**
194
201
  Interface for objects returned by completion sources.
195
202
  */
@@ -230,12 +237,15 @@ interface CompletionResult {
230
237
  filter?: boolean;
231
238
  /**
232
239
  When [`filter`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.filter) is set to
233
- `false`, this may be provided to compute the ranges on the label
234
- that match the input. Should return an array of numbers where
235
- each pair of adjacent numbers provide the start and end of a
236
- range.
240
+ `false` or a completion has a
241
+ [`displayLabel`](https://codemirror.net/6/docs/ref/#autocomplete.Completion.displayLabel), this
242
+ may be provided to compute the ranges on the label that match
243
+ the input. Should return an array of numbers where each pair of
244
+ adjacent numbers provide the start and end of a range. The
245
+ second argument, the match found by the library, is only passed
246
+ when `filter` isn't `false`.
237
247
  */
238
- getMatch?: (completion: Completion) => readonly number[];
248
+ getMatch?: (completion: Completion, matched?: readonly number[]) => readonly number[];
239
249
  /**
240
250
  Synchronously update the completion result after typing or
241
251
  deletion. If given, this should not do any expensive work, since
@@ -334,13 +344,13 @@ interface CompletionConfig {
334
344
  position: number;
335
345
  }[];
336
346
  /**
337
- By default, [info](https://codemirror.net/6/docs/ref/#autocomplet.Completion.info) tooltips are
338
- placed to the side of the selected. This option can be used to
339
- override that. It will be given rectangles for the list of
340
- completions, the selected option, the info element, and the
341
- availble [tooltip space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace),
342
- and should return style and/or class strings for the info
343
- 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.
344
354
  */
345
355
  positionInfo?: (view: EditorView, list: Rect, option: Rect, info: Rect, space: Rect) => {
346
356
  style?: string;
package/dist/index.d.ts CHANGED
@@ -9,11 +9,18 @@ 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;
16
16
  /**
17
+ An optional override for the completion's visible label. When
18
+ using this, matched characters will only be highlighted if you
19
+ provide a [`getMatch`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.getMatch)
20
+ function.
21
+ */
22
+ displayLabel?: string;
23
+ /**
17
24
  An optional short piece of information to show (with a different
18
25
  style) after the label.
19
26
  */
@@ -68,7 +75,7 @@ The type returned from
68
75
  node, null to indicate there is no info, or an object with an
69
76
  optional `destroy` method that cleans up the node.
70
77
  */
71
- declare type CompletionInfo = Node | null | {
78
+ type CompletionInfo = Node | null | {
72
79
  dom: Node;
73
80
  destroy?(): void;
74
81
  };
@@ -189,7 +196,7 @@ may return its [result](https://codemirror.net/6/docs/ref/#autocomplete.Completi
189
196
  synchronously or as a promise. Returning null indicates no
190
197
  completions are available.
191
198
  */
192
- declare type CompletionSource = (context: CompletionContext) => CompletionResult | null | Promise<CompletionResult | null>;
199
+ type CompletionSource = (context: CompletionContext) => CompletionResult | null | Promise<CompletionResult | null>;
193
200
  /**
194
201
  Interface for objects returned by completion sources.
195
202
  */
@@ -230,12 +237,15 @@ interface CompletionResult {
230
237
  filter?: boolean;
231
238
  /**
232
239
  When [`filter`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.filter) is set to
233
- `false`, this may be provided to compute the ranges on the label
234
- that match the input. Should return an array of numbers where
235
- each pair of adjacent numbers provide the start and end of a
236
- range.
240
+ `false` or a completion has a
241
+ [`displayLabel`](https://codemirror.net/6/docs/ref/#autocomplete.Completion.displayLabel), this
242
+ may be provided to compute the ranges on the label that match
243
+ the input. Should return an array of numbers where each pair of
244
+ adjacent numbers provide the start and end of a range. The
245
+ second argument, the match found by the library, is only passed
246
+ when `filter` isn't `false`.
237
247
  */
238
- getMatch?: (completion: Completion) => readonly number[];
248
+ getMatch?: (completion: Completion, matched?: readonly number[]) => readonly number[];
239
249
  /**
240
250
  Synchronously update the completion result after typing or
241
251
  deletion. If given, this should not do any expensive work, since
@@ -334,13 +344,13 @@ interface CompletionConfig {
334
344
  position: number;
335
345
  }[];
336
346
  /**
337
- By default, [info](https://codemirror.net/6/docs/ref/#autocomplet.Completion.info) tooltips are
338
- placed to the side of the selected. This option can be used to
339
- override that. It will be given rectangles for the list of
340
- completions, the selected option, the info element, and the
341
- availble [tooltip space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace),
342
- and should return style and/or class strings for the info
343
- 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.
344
354
  */
345
355
  positionInfo?: (view: EditorView, list: Rect, option: Rect, info: Rect, space: Rect) => {
346
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) {
@@ -198,6 +198,8 @@ class FuzzyMatcher {
198
198
  this.any = [];
199
199
  this.precise = [];
200
200
  this.byWord = [];
201
+ this.score = 0;
202
+ this.matched = [];
201
203
  for (let p = 0; p < pattern.length;) {
202
204
  let char = codePointAt(pattern, p), size = codePointSize(char);
203
205
  this.chars.push(char);
@@ -207,34 +209,39 @@ class FuzzyMatcher {
207
209
  }
208
210
  this.astral = pattern.length != this.chars.length;
209
211
  }
212
+ ret(score, matched) {
213
+ this.score = score;
214
+ this.matched = matched;
215
+ return true;
216
+ }
210
217
  // Matches a given word (completion) against the pattern (input).
211
- // Will return null for no match, and otherwise an array that starts
212
- // with the match score, followed by any number of `from, to` pairs
213
- // indicating the matched parts of `word`.
218
+ // Will return a boolean indicating whether there was a match and,
219
+ // on success, set `this.score` to the score, `this.matched` to an
220
+ // array of `from, to` pairs indicating the matched parts of `word`.
214
221
  //
215
222
  // The score is a number that is more negative the worse the match
216
223
  // is. See `Penalty` above.
217
224
  match(word) {
218
225
  if (this.pattern.length == 0)
219
- return [-100 /* NotFull */];
226
+ return this.ret(-100 /* Penalty.NotFull */, []);
220
227
  if (word.length < this.pattern.length)
221
- return null;
228
+ return false;
222
229
  let { chars, folded, any, precise, byWord } = this;
223
230
  // For single-character queries, only match when they occur right
224
231
  // at the start
225
232
  if (chars.length == 1) {
226
233
  let first = codePointAt(word, 0), firstSize = codePointSize(first);
227
- let score = firstSize == word.length ? 0 : -100 /* NotFull */;
234
+ let score = firstSize == word.length ? 0 : -100 /* Penalty.NotFull */;
228
235
  if (first == chars[0]) ;
229
236
  else if (first == folded[0])
230
- score += -200 /* CaseFold */;
237
+ score += -200 /* Penalty.CaseFold */;
231
238
  else
232
- return null;
233
- return [score, 0, firstSize];
239
+ return false;
240
+ return this.ret(score, [0, firstSize]);
234
241
  }
235
242
  let direct = word.indexOf(this.pattern);
236
243
  if (direct == 0)
237
- return [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]);
238
245
  let len = chars.length, anyTo = 0;
239
246
  if (direct < 0) {
240
247
  for (let i = 0, e = Math.min(word.length, 200); i < e && anyTo < len;) {
@@ -245,7 +252,7 @@ class FuzzyMatcher {
245
252
  }
246
253
  // No match, exit immediately
247
254
  if (anyTo < len)
248
- return null;
255
+ return false;
249
256
  }
250
257
  // This tracks the extent of the precise (non-folded, not
251
258
  // necessarily adjacent) match
@@ -258,7 +265,7 @@ class FuzzyMatcher {
258
265
  let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
259
266
  let hasLower = /[a-z]/.test(word), wordAdjacent = true;
260
267
  // Go over the option's text, scanning for the various kinds of matches
261
- 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;) {
262
269
  let next = codePointAt(word, i);
263
270
  if (direct < 0) {
264
271
  if (preciseTo < len && next == chars[preciseTo])
@@ -276,9 +283,9 @@ class FuzzyMatcher {
276
283
  }
277
284
  }
278
285
  let ch, type = next < 0xff
279
- ? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Lower */ : next >= 65 && next <= 90 ? 1 /* Upper */ : 0 /* NonWord */)
280
- : ((ch = fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Upper */ : ch != ch.toUpperCase() ? 2 /* Lower */ : 0 /* NonWord */);
281
- 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 */) {
282
289
  if (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
283
290
  byWord[byWordTo++] = i;
284
291
  else if (byWord.length)
@@ -288,30 +295,31 @@ class FuzzyMatcher {
288
295
  i += codePointSize(next);
289
296
  }
290
297
  if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
291
- 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);
292
299
  if (adjacentTo == len && adjacentStart == 0)
293
- return [-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]);
294
301
  if (direct > -1)
295
- return [-700 /* NotStart */ - word.length, direct, direct + this.pattern.length];
302
+ return this.ret(-700 /* Penalty.NotStart */ - word.length, [direct, direct + this.pattern.length]);
296
303
  if (adjacentTo == len)
297
- return [-200 /* CaseFold */ + -700 /* NotStart */ - word.length, adjacentStart, adjacentEnd];
304
+ return this.ret(-200 /* Penalty.CaseFold */ + -700 /* Penalty.NotStart */ - word.length, [adjacentStart, adjacentEnd]);
298
305
  if (byWordTo == len)
299
- return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */ +
300
- (wordAdjacent ? 0 : -1100 /* Gap */), byWord, word);
301
- return chars.length == 2 ? null : this.result((any[0] ? -700 /* NotStart */ : 0) + -200 /* CaseFold */ + -1100 /* Gap */, any, 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
+ return chars.length == 2 ? false
309
+ : this.result((any[0] ? -700 /* Penalty.NotStart */ : 0) + -200 /* Penalty.CaseFold */ + -1100 /* Penalty.Gap */, any, word);
302
310
  }
303
311
  result(score, positions, word) {
304
- let result = [score - word.length], i = 1;
312
+ let result = [], i = 0;
305
313
  for (let pos of positions) {
306
314
  let to = pos + (this.astral ? codePointSize(codePointAt(word, pos)) : 1);
307
- if (i > 1 && result[i - 1] == pos)
315
+ if (i && result[i - 1] == pos)
308
316
  result[i - 1] = to;
309
317
  else {
310
318
  result[i++] = pos;
311
319
  result[i++] = to;
312
320
  }
313
321
  }
314
- return result;
322
+ return this.ret(score - word.length, result);
315
323
  }
316
324
  }
317
325
 
@@ -345,7 +353,7 @@ const completionConfig = /*@__PURE__*/Facet.define({
345
353
  function joinClass(a, b) {
346
354
  return a ? b ? a + " " + b : a : b;
347
355
  }
348
- function defaultPositionInfo(view, list, option, info, space) {
356
+ function defaultPositionInfo(view, list, option, info, space, tooltip) {
349
357
  let rtl = view.textDirection == Direction.RTL, left = rtl, narrow = false;
350
358
  let side = "top", offset, maxWidth;
351
359
  let spaceLeft = list.left - space.left, spaceRight = space.right - list.right;
@@ -356,11 +364,11 @@ function defaultPositionInfo(view, list, option, info, space) {
356
364
  left = true;
357
365
  if (infoWidth <= (left ? spaceLeft : spaceRight)) {
358
366
  offset = Math.max(space.top, Math.min(option.top, space.bottom - infoHeight)) - list.top;
359
- maxWidth = Math.min(400 /* Width */, left ? spaceLeft : spaceRight);
367
+ maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight);
360
368
  }
361
369
  else {
362
370
  narrow = true;
363
- 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 */);
364
372
  let spaceBelow = space.bottom - list.bottom;
365
373
  if (spaceBelow >= infoHeight || spaceBelow > list.top) { // Below the completion
366
374
  offset = option.bottom - list.top;
@@ -370,8 +378,10 @@ function defaultPositionInfo(view, list, option, info, space) {
370
378
  offset = list.bottom - option.top;
371
379
  }
372
380
  }
381
+ let scaleY = (list.bottom - list.top) / tooltip.offsetHeight;
382
+ let scaleX = (list.right - list.left) / tooltip.offsetWidth;
373
383
  return {
374
- style: `${side}: ${offset}px; max-width: ${maxWidth}px`,
384
+ style: `${side}: ${offset / scaleY}px; max-width: ${maxWidth / scaleX}px`,
375
385
  class: "cm-completionInfo-" + (narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right")
376
386
  };
377
387
  }
@@ -394,8 +404,8 @@ function optionContent(config) {
394
404
  render(completion, _s, match) {
395
405
  let labelElt = document.createElement("span");
396
406
  labelElt.className = "cm-completionLabel";
397
- let { label } = completion, off = 0;
398
- for (let j = 1; j < match.length;) {
407
+ let label = completion.displayLabel || completion.label, off = 0;
408
+ for (let j = 0; j < match.length;) {
399
409
  let from = match[j++], to = match[j++];
400
410
  if (from > off)
401
411
  labelElt.appendChild(document.createTextNode(label.slice(off, from)));
@@ -591,7 +601,7 @@ class CompletionTooltip {
591
601
  if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 ||
592
602
  selRect.bottom < Math.max(space.top, listRect.top) + 10)
593
603
  return null;
594
- return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space);
604
+ return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space, this.dom);
595
605
  }
596
606
  placeInfo(pos) {
597
607
  if (this.info) {
@@ -665,10 +675,11 @@ function completionTooltip(stateField, applyCompletion) {
665
675
  function scrollIntoView(container, element) {
666
676
  let parent = container.getBoundingClientRect();
667
677
  let self = element.getBoundingClientRect();
678
+ let scaleY = parent.height / container.offsetHeight;
668
679
  if (self.top < parent.top)
669
- container.scrollTop -= parent.top - self.top;
680
+ container.scrollTop -= (parent.top - self.top) / scaleY;
670
681
  else if (self.bottom > parent.bottom)
671
- container.scrollTop += self.bottom - parent.bottom;
682
+ container.scrollTop += (self.bottom - parent.bottom) / scaleY;
672
683
  }
673
684
 
674
685
  // Used to pick a preferred option when two options with the same
@@ -693,21 +704,18 @@ function sortOptions(active, state) {
693
704
  };
694
705
  for (let a of active)
695
706
  if (a.hasResult()) {
707
+ let getMatch = a.result.getMatch;
696
708
  if (a.result.filter === false) {
697
- let getMatch = a.result.getMatch;
698
709
  for (let option of a.result.options) {
699
- let match = [1e9 - options.length];
700
- if (getMatch)
701
- for (let n of getMatch(option))
702
- match.push(n);
703
- addOption(new Option(option, a.source, match, match[0]));
710
+ addOption(new Option(option, a.source, getMatch ? getMatch(option) : [], 1e9 - options.length));
704
711
  }
705
712
  }
706
713
  else {
707
- let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match;
714
+ let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to));
708
715
  for (let option of a.result.options)
709
- if (match = matcher.match(option.label)) {
710
- addOption(new Option(option, a.source, match, match[0] + (option.boost || 0)));
716
+ if (matcher.match(option.label)) {
717
+ let matched = !option.displayLabel ? matcher.matched : getMatch ? getMatch(option, matcher.matched) : [];
718
+ addOption(new Option(option, a.source, matched, matcher.score + (option.boost || 0)));
711
719
  }
712
720
  }
713
721
  }
@@ -754,7 +762,7 @@ class CompletionDialog {
754
762
  static build(active, state, id, prev, conf) {
755
763
  let options = sortOptions(active, state);
756
764
  if (!options.length) {
757
- return prev && active.some(a => a.state == 1 /* Pending */) ?
765
+ return prev && active.some(a => a.state == 1 /* State.Pending */) ?
758
766
  new CompletionDialog(prev.options, prev.attrs, prev.tooltip, prev.timestamp, prev.selected, true) : null;
759
767
  }
760
768
  let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
@@ -791,7 +799,7 @@ class CompletionState {
791
799
  state.languageDataAt("autocomplete", cur(state)).map(asSource);
792
800
  let active = sources.map(source => {
793
801
  let value = this.active.find(s => s.source == source) ||
794
- new ActiveSource(source, this.active.some(a => a.state != 0 /* Inactive */) ? 1 /* Pending */ : 0 /* Inactive */);
802
+ new ActiveSource(source, this.active.some(a => a.state != 0 /* State.Inactive */) ? 1 /* State.Pending */ : 0 /* State.Inactive */);
795
803
  return value.update(tr, conf);
796
804
  });
797
805
  if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
@@ -802,10 +810,10 @@ class CompletionState {
802
810
  if (tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
803
811
  !sameResults(active, this.active))
804
812
  open = CompletionDialog.build(active, state, this.id, open, conf);
805
- else if (open && open.disabled && !active.some(a => a.state == 1 /* Pending */))
813
+ else if (open && open.disabled && !active.some(a => a.state == 1 /* State.Pending */))
806
814
  open = null;
807
- if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
808
- active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
815
+ if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult()))
816
+ active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* State.Inactive */) : a);
809
817
  for (let effect of tr.effects)
810
818
  if (effect.is(setSelectedEffect))
811
819
  open = open && open.setSelected(effect.value, this.id);
@@ -859,13 +867,13 @@ class ActiveSource {
859
867
  value = value.handleUserEvent(tr, event, conf);
860
868
  else if (tr.docChanged)
861
869
  value = value.handleChange(tr);
862
- else if (tr.selection && value.state != 0 /* Inactive */)
863
- value = new ActiveSource(value.source, 0 /* Inactive */);
870
+ else if (tr.selection && value.state != 0 /* State.Inactive */)
871
+ value = new ActiveSource(value.source, 0 /* State.Inactive */);
864
872
  for (let effect of tr.effects) {
865
873
  if (effect.is(startCompletionEffect))
866
- value = new ActiveSource(value.source, 1 /* Pending */, effect.value ? cur(tr.state) : -1);
874
+ value = new ActiveSource(value.source, 1 /* State.Pending */, effect.value ? cur(tr.state) : -1);
867
875
  else if (effect.is(closeCompletionEffect))
868
- value = new ActiveSource(value.source, 0 /* Inactive */);
876
+ value = new ActiveSource(value.source, 0 /* State.Inactive */);
869
877
  else if (effect.is(setActiveEffect))
870
878
  for (let active of effect.value)
871
879
  if (active.source == value.source)
@@ -874,10 +882,10 @@ class ActiveSource {
874
882
  return value;
875
883
  }
876
884
  handleUserEvent(tr, type, conf) {
877
- return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* Pending */);
885
+ return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* State.Pending */);
878
886
  }
879
887
  handleChange(tr) {
880
- return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
888
+ return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
881
889
  }
882
890
  map(changes) {
883
891
  return changes.empty || this.explicitPos < 0 ? this : new ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos));
@@ -885,7 +893,7 @@ class ActiveSource {
885
893
  }
886
894
  class ActiveResult extends ActiveSource {
887
895
  constructor(source, explicitPos, result, from, to) {
888
- super(source, 2 /* Result */, explicitPos);
896
+ super(source, 2 /* State.Result */, explicitPos);
889
897
  this.result = result;
890
898
  this.from = from;
891
899
  this.to = to;
@@ -898,17 +906,17 @@ class ActiveResult extends ActiveSource {
898
906
  if ((this.explicitPos < 0 ? pos <= from : pos < this.from) ||
899
907
  pos > to ||
900
908
  type == "delete" && cur(tr.startState) == this.from)
901
- return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */);
909
+ return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* State.Pending */ : 0 /* State.Inactive */);
902
910
  let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos), updated;
903
911
  if (checkValid(this.result.validFor, tr.state, from, to))
904
912
  return new ActiveResult(this.source, explicitPos, this.result, from, to);
905
913
  if (this.result.update &&
906
914
  (updated = this.result.update(this.result, from, to, new CompletionContext(tr.state, pos, explicitPos >= 0))))
907
915
  return new ActiveResult(this.source, explicitPos, updated, updated.from, (_a = updated.to) !== null && _a !== void 0 ? _a : cur(tr.state));
908
- return new ActiveSource(this.source, 1 /* Pending */, explicitPos);
916
+ return new ActiveSource(this.source, 1 /* State.Pending */, explicitPos);
909
917
  }
910
918
  handleChange(tr) {
911
- return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
919
+ return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
912
920
  }
913
921
  map(mapping) {
914
922
  return mapping.empty ? this :
@@ -994,7 +1002,7 @@ Close the currently active completion.
994
1002
  */
995
1003
  const closeCompletion = (view) => {
996
1004
  let cState = view.state.field(completionState, false);
997
- if (!cState || !cState.active.some(a => a.state != 0 /* Inactive */))
1005
+ if (!cState || !cState.active.some(a => a.state != 0 /* State.Inactive */))
998
1006
  return false;
999
1007
  view.dispatch({ effects: closeCompletionEffect.of(null) });
1000
1008
  return true;
@@ -1017,9 +1025,9 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
1017
1025
  this.debounceUpdate = -1;
1018
1026
  this.running = [];
1019
1027
  this.debounceAccept = -1;
1020
- this.composing = 0 /* None */;
1028
+ this.composing = 0 /* CompositionState.None */;
1021
1029
  for (let active of view.state.field(completionState).active)
1022
- if (active.state == 1 /* Pending */)
1030
+ if (active.state == 1 /* State.Pending */)
1023
1031
  this.startQuery(active);
1024
1032
  }
1025
1033
  update(update) {
@@ -1050,21 +1058,21 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
1050
1058
  }
1051
1059
  if (this.debounceUpdate > -1)
1052
1060
  clearTimeout(this.debounceUpdate);
1053
- this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.active.source == a.source))
1061
+ this.debounceUpdate = cState.active.some(a => a.state == 1 /* State.Pending */ && !this.running.some(q => q.active.source == a.source))
1054
1062
  ? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
1055
- if (this.composing != 0 /* None */)
1063
+ if (this.composing != 0 /* CompositionState.None */)
1056
1064
  for (let tr of update.transactions) {
1057
1065
  if (getUserEvent(tr) == "input")
1058
- this.composing = 2 /* Changed */;
1059
- else if (this.composing == 2 /* Changed */ && tr.selection)
1060
- this.composing = 3 /* ChangedAndMoved */;
1066
+ this.composing = 2 /* CompositionState.Changed */;
1067
+ else if (this.composing == 2 /* CompositionState.Changed */ && tr.selection)
1068
+ this.composing = 3 /* CompositionState.ChangedAndMoved */;
1061
1069
  }
1062
1070
  }
1063
1071
  startUpdate() {
1064
1072
  this.debounceUpdate = -1;
1065
1073
  let { state } = this.view, cState = state.field(completionState);
1066
1074
  for (let active of cState.active) {
1067
- if (active.state == 1 /* Pending */ && !this.running.some(r => r.active.source == active.source))
1075
+ if (active.state == 1 /* State.Pending */ && !this.running.some(r => r.active.source == active.source))
1068
1076
  this.startQuery(active);
1069
1077
  }
1070
1078
  }
@@ -1115,14 +1123,14 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
1115
1123
  }
1116
1124
  }
1117
1125
  let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
1118
- if (current && current.state == 1 /* Pending */) {
1126
+ if (current && current.state == 1 /* State.Pending */) {
1119
1127
  if (query.done == null) {
1120
1128
  // Explicitly failed. Should clear the pending status if it
1121
1129
  // hasn't been re-set in the meantime.
1122
- let active = new ActiveSource(query.active.source, 0 /* Inactive */);
1130
+ let active = new ActiveSource(query.active.source, 0 /* State.Inactive */);
1123
1131
  for (let tr of query.updates)
1124
1132
  active = active.update(tr, conf);
1125
- if (active.state != 1 /* Pending */)
1133
+ if (active.state != 1 /* State.Pending */)
1126
1134
  updated.push(active);
1127
1135
  }
1128
1136
  else {
@@ -1145,15 +1153,15 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
1145
1153
  }
1146
1154
  },
1147
1155
  compositionstart() {
1148
- this.composing = 1 /* Started */;
1156
+ this.composing = 1 /* CompositionState.Started */;
1149
1157
  },
1150
1158
  compositionend() {
1151
- if (this.composing == 3 /* ChangedAndMoved */) {
1159
+ if (this.composing == 3 /* CompositionState.ChangedAndMoved */) {
1152
1160
  // Safari fires compositionend events synchronously, possibly
1153
1161
  // from inside an update, so dispatch asynchronously to avoid reentrancy
1154
1162
  setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
1155
1163
  }
1156
- this.composing = 0 /* None */;
1164
+ this.composing = 0 /* CompositionState.None */;
1157
1165
  }
1158
1166
  }
1159
1167
  });
@@ -1213,13 +1221,13 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
1213
1221
  position: "absolute",
1214
1222
  padding: "3px 9px",
1215
1223
  width: "max-content",
1216
- maxWidth: `${400 /* Width */}px`,
1224
+ maxWidth: `${400 /* Info.Width */}px`,
1217
1225
  boxSizing: "border-box"
1218
1226
  },
1219
1227
  ".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
1220
1228
  ".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
1221
- ".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Margin */}px` },
1222
- ".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Margin */}px` },
1229
+ ".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Info.Margin */}px` },
1230
+ ".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Info.Margin */}px` },
1223
1231
  "&light .cm-snippetField": { backgroundColor: "#00000022" },
1224
1232
  "&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
1225
1233
  ".cm-snippetFieldPosition": {
@@ -1470,7 +1478,8 @@ function moveField(dir) {
1470
1478
  let next = active.active + dir, last = dir > 0 && !active.ranges.some(r => r.field == next + dir);
1471
1479
  dispatch(state.update({
1472
1480
  selection: fieldSelection(active.ranges, next),
1473
- effects: setActive.of(last ? null : new ActiveSnippet(active.ranges, next))
1481
+ effects: setActive.of(last ? null : new ActiveSnippet(active.ranges, next)),
1482
+ scrollIntoView: true
1474
1483
  }));
1475
1484
  return true;
1476
1485
  };
@@ -1542,7 +1551,9 @@ const snippetPointerHandler = /*@__PURE__*/EditorView.domEventHandlers({
1542
1551
  return false;
1543
1552
  view.dispatch({
1544
1553
  selection: fieldSelection(active.ranges, match.field),
1545
- effects: setActive.of(active.ranges.some(r => r.field > match.field) ? new ActiveSnippet(active.ranges, match.field) : null)
1554
+ effects: setActive.of(active.ranges.some(r => r.field > match.field)
1555
+ ? new ActiveSnippet(active.ranges, match.field) : null),
1556
+ scrollIntoView: true
1546
1557
  });
1547
1558
  return true;
1548
1559
  }
@@ -1572,7 +1583,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
1572
1583
  if (!seen[m[0]] && pos + m.index != ignoreAt) {
1573
1584
  result.push({ type: "text", label: m[0] });
1574
1585
  seen[m[0]] = true;
1575
- if (result.length >= 2000 /* MaxList */)
1586
+ if (result.length >= 2000 /* C.MaxList */)
1576
1587
  return;
1577
1588
  }
1578
1589
  }
@@ -1580,7 +1591,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
1580
1591
  }
1581
1592
  }
1582
1593
  function collectWords(doc, cache, wordRE, to, ignoreAt) {
1583
- let big = doc.length >= 1000 /* MinCacheLen */;
1594
+ let big = doc.length >= 1000 /* C.MinCacheLen */;
1584
1595
  let cached = big && cache.get(doc);
1585
1596
  if (cached)
1586
1597
  return cached;
@@ -1588,7 +1599,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
1588
1599
  if (doc.children) {
1589
1600
  let pos = 0;
1590
1601
  for (let ch of doc.children) {
1591
- if (ch.length >= 1000 /* MinCacheLen */) {
1602
+ if (ch.length >= 1000 /* C.MinCacheLen */) {
1592
1603
  for (let c of collectWords(ch, cache, wordRE, to - pos, ignoreAt - pos)) {
1593
1604
  if (!seen[c.label]) {
1594
1605
  seen[c.label] = true;
@@ -1605,7 +1616,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
1605
1616
  else {
1606
1617
  storeWords(doc, wordRE, result, seen, ignoreAt);
1607
1618
  }
1608
- if (big && result.length < 2000 /* MaxList */)
1619
+ if (big && result.length < 2000 /* C.MaxList */)
1609
1620
  cache.set(doc, result);
1610
1621
  return result;
1611
1622
  }
@@ -1621,7 +1632,7 @@ const completeAnyWord = context => {
1621
1632
  if (!token && !context.explicit)
1622
1633
  return null;
1623
1634
  let from = token ? token.from : context.pos;
1624
- let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* Range */, from);
1635
+ let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* C.Range */, from);
1625
1636
  return { from, options, validFor: mapRE(re, s => "^" + s) };
1626
1637
  };
1627
1638
 
@@ -1913,8 +1924,8 @@ returns `null`.
1913
1924
  */
1914
1925
  function completionStatus(state) {
1915
1926
  let cState = state.field(completionState, false);
1916
- return cState && cState.active.some(a => a.state == 1 /* Pending */) ? "pending"
1917
- : cState && cState.active.some(a => a.state != 0 /* Inactive */) ? "active" : null;
1927
+ return cState && cState.active.some(a => a.state == 1 /* State.Pending */) ? "pending"
1928
+ : cState && cState.active.some(a => a.state != 0 /* State.Inactive */) ? "active" : null;
1918
1929
  }
1919
1930
  const completionArrayCache = /*@__PURE__*/new WeakMap;
1920
1931
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/autocomplete",
3
- "version": "6.8.1",
3
+ "version": "6.9.1",
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": {