@codemirror/autocomplete 0.19.1 → 0.19.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { Facet, combineConfig, StateEffect, StateField, Text, EditorSelection, Prec, CharCategory } from '@codemirror/state';
2
- import { EditorView, Direction, logException, ViewPlugin, Decoration, WidgetType, keymap } from '@codemirror/view';
1
+ import { Annotation, Facet, combineConfig, StateEffect, StateField, Prec, EditorSelection, Text } from '@codemirror/state';
2
+ import { Direction, logException, EditorView, ViewPlugin, Decoration, WidgetType, keymap } from '@codemirror/view';
3
3
  import { showTooltip } from '@codemirror/tooltip';
4
4
  import { syntaxTree, indentUnit } from '@codemirror/language';
5
5
  import { codePointAt, codePointSize, fromCodePoint } from '@codemirror/text';
@@ -146,6 +146,11 @@ function ensureAnchor(expr, start) {
146
146
  return expr;
147
147
  return new RegExp(`${addStart ? "^" : ""}(?:${source})${addEnd ? "$" : ""}`, (_a = expr.flags) !== null && _a !== void 0 ? _a : (expr.ignoreCase ? "i" : ""));
148
148
  }
149
+ /**
150
+ This annotation is added to transactions that are produced by
151
+ picking a completion.
152
+ */
153
+ const pickedCompletion = /*@__PURE__*/Annotation.define();
149
154
  function applyCompletion(view, option) {
150
155
  let apply = option.completion.apply || option.completion.label;
151
156
  let result = option.source;
@@ -153,7 +158,8 @@ function applyCompletion(view, option) {
153
158
  view.dispatch({
154
159
  changes: { from: result.from, to: result.to, insert: apply },
155
160
  selection: { anchor: result.from + apply.length },
156
- userEvent: "input.complete"
161
+ userEvent: "input.complete",
162
+ annotations: pickedCompletion.of(option.completion)
157
163
  });
158
164
  }
159
165
  else {
@@ -247,7 +253,7 @@ class FuzzyMatcher {
247
253
  if (next == chars[adjacentTo] || next == folded[adjacentTo]) {
248
254
  if (adjacentTo == 0)
249
255
  adjacentStart = i;
250
- adjacentEnd = i;
256
+ adjacentEnd = i + 1;
251
257
  adjacentTo++;
252
258
  }
253
259
  else {
@@ -297,152 +303,67 @@ const completionConfig = /*@__PURE__*/Facet.define({
297
303
  activateOnTyping: true,
298
304
  override: null,
299
305
  maxRenderedOptions: 100,
300
- defaultKeymap: true
306
+ defaultKeymap: true,
307
+ optionClass: () => "",
308
+ icons: true,
309
+ addToOptions: []
301
310
  }, {
302
- defaultKeymap: (a, b) => a && b
311
+ defaultKeymap: (a, b) => a && b,
312
+ icons: (a, b) => a && b,
313
+ optionClass: (a, b) => c => joinClass(a(c), b(c)),
314
+ addToOptions: (a, b) => a.concat(b)
303
315
  });
304
316
  }
305
317
  });
318
+ function joinClass(a, b) {
319
+ return a ? b ? a + " " + b : a : b;
320
+ }
306
321
 
307
- const MaxInfoWidth = 300;
308
- const baseTheme = /*@__PURE__*/EditorView.baseTheme({
309
- ".cm-tooltip.cm-tooltip-autocomplete": {
310
- "& > ul": {
311
- fontFamily: "monospace",
312
- whiteSpace: "nowrap",
313
- overflow: "auto",
314
- maxWidth_fallback: "700px",
315
- maxWidth: "min(700px, 95vw)",
316
- maxHeight: "10em",
317
- listStyle: "none",
318
- margin: 0,
319
- padding: 0,
320
- "& > li": {
321
- cursor: "pointer",
322
- padding: "1px 1em 1px 3px",
323
- lineHeight: 1.2
322
+ function optionContent(config) {
323
+ let content = config.addToOptions.slice();
324
+ if (config.icons)
325
+ content.push({
326
+ render(completion) {
327
+ let icon = document.createElement("div");
328
+ icon.classList.add("cm-completionIcon");
329
+ if (completion.type)
330
+ icon.classList.add(...completion.type.split(/\s+/g).map(cls => "cm-completionIcon-" + cls));
331
+ icon.setAttribute("aria-hidden", "true");
332
+ return icon;
324
333
  },
325
- "& > li[aria-selected]": {
326
- background_fallback: "#bdf",
327
- backgroundColor: "Highlight",
328
- color_fallback: "white",
329
- color: "HighlightText"
334
+ position: 20
335
+ });
336
+ content.push({
337
+ render(completion, _s, match) {
338
+ let labelElt = document.createElement("span");
339
+ labelElt.className = "cm-completionLabel";
340
+ let { label } = completion, off = 0;
341
+ for (let j = 1; j < match.length;) {
342
+ let from = match[j++], to = match[j++];
343
+ if (from > off)
344
+ labelElt.appendChild(document.createTextNode(label.slice(off, from)));
345
+ let span = labelElt.appendChild(document.createElement("span"));
346
+ span.appendChild(document.createTextNode(label.slice(from, to)));
347
+ span.className = "cm-completionMatchedText";
348
+ off = to;
330
349
  }
331
- }
332
- },
333
- ".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after": {
334
- content: '"···"',
335
- opacity: 0.5,
336
- display: "block",
337
- textAlign: "center"
338
- },
339
- ".cm-tooltip.cm-completionInfo": {
340
- position: "absolute",
341
- padding: "3px 9px",
342
- width: "max-content",
343
- maxWidth: MaxInfoWidth + "px",
344
- },
345
- ".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
346
- ".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
347
- "&light .cm-snippetField": { backgroundColor: "#00000022" },
348
- "&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
349
- ".cm-snippetFieldPosition": {
350
- verticalAlign: "text-top",
351
- width: 0,
352
- height: "1.15em",
353
- margin: "0 -0.7px -.7em",
354
- borderLeft: "1.4px dotted #888"
355
- },
356
- ".cm-completionMatchedText": {
357
- textDecoration: "underline"
358
- },
359
- ".cm-completionDetail": {
360
- marginLeft: "0.5em",
361
- fontStyle: "italic"
362
- },
363
- ".cm-completionIcon": {
364
- fontSize: "90%",
365
- width: ".8em",
366
- display: "inline-block",
367
- textAlign: "center",
368
- paddingRight: ".6em",
369
- opacity: "0.6"
370
- },
371
- ".cm-completionIcon-function, .cm-completionIcon-method": {
372
- "&:after": { content: "'ƒ'" }
373
- },
374
- ".cm-completionIcon-class": {
375
- "&:after": { content: "'○'" }
376
- },
377
- ".cm-completionIcon-interface": {
378
- "&:after": { content: "'◌'" }
379
- },
380
- ".cm-completionIcon-variable": {
381
- "&:after": { content: "'𝑥'" }
382
- },
383
- ".cm-completionIcon-constant": {
384
- "&:after": { content: "'𝐶'" }
385
- },
386
- ".cm-completionIcon-type": {
387
- "&:after": { content: "'𝑡'" }
388
- },
389
- ".cm-completionIcon-enum": {
390
- "&:after": { content: "'∪'" }
391
- },
392
- ".cm-completionIcon-property": {
393
- "&:after": { content: "'□'" }
394
- },
395
- ".cm-completionIcon-keyword": {
396
- "&:after": { content: "'🔑\uFE0E'" } // Disable emoji rendering
397
- },
398
- ".cm-completionIcon-namespace": {
399
- "&:after": { content: "'▢'" }
400
- },
401
- ".cm-completionIcon-text": {
402
- "&:after": { content: "'abc'", fontSize: "50%", verticalAlign: "middle" }
403
- }
404
- });
405
-
406
- function createListBox(options, id, range) {
407
- const ul = document.createElement("ul");
408
- ul.id = id;
409
- ul.setAttribute("role", "listbox");
410
- ul.setAttribute("aria-expanded", "true");
411
- for (let i = range.from; i < range.to; i++) {
412
- let { completion, match } = options[i];
413
- const li = ul.appendChild(document.createElement("li"));
414
- li.id = id + "-" + i;
415
- let icon = li.appendChild(document.createElement("div"));
416
- icon.classList.add("cm-completionIcon");
417
- if (completion.type)
418
- icon.classList.add(...completion.type.split(/\s+/g).map(cls => "cm-completionIcon-" + cls));
419
- icon.setAttribute("aria-hidden", "true");
420
- let labelElt = li.appendChild(document.createElement("span"));
421
- labelElt.className = "cm-completionLabel";
422
- let { label, detail } = completion, off = 0;
423
- for (let j = 1; j < match.length;) {
424
- let from = match[j++], to = match[j++];
425
- if (from > off)
426
- labelElt.appendChild(document.createTextNode(label.slice(off, from)));
427
- let span = labelElt.appendChild(document.createElement("span"));
428
- span.appendChild(document.createTextNode(label.slice(from, to)));
429
- span.className = "cm-completionMatchedText";
430
- off = to;
431
- }
432
- if (off < label.length)
433
- labelElt.appendChild(document.createTextNode(label.slice(off)));
434
- if (detail) {
435
- let detailElt = li.appendChild(document.createElement("span"));
350
+ if (off < label.length)
351
+ labelElt.appendChild(document.createTextNode(label.slice(off)));
352
+ return labelElt;
353
+ },
354
+ position: 50
355
+ }, {
356
+ render(completion) {
357
+ if (!completion.detail)
358
+ return null;
359
+ let detailElt = document.createElement("span");
436
360
  detailElt.className = "cm-completionDetail";
437
- detailElt.textContent = detail;
438
- }
439
- li.setAttribute("role", "option");
440
- }
441
- if (range.from)
442
- ul.classList.add("cm-completionListIncompleteTop");
443
- if (range.to < options.length)
444
- ul.classList.add("cm-completionListIncompleteBottom");
445
- return ul;
361
+ detailElt.textContent = completion.detail;
362
+ return detailElt;
363
+ },
364
+ position: 80
365
+ });
366
+ return content.sort((a, b) => a.position - b.position).map(a => a.render);
446
367
  }
447
368
  function createInfoDialog(option, view) {
448
369
  let dom = document.createElement("div");
@@ -483,6 +404,8 @@ class CompletionTooltip {
483
404
  let cState = view.state.field(stateField);
484
405
  let { options, selected } = cState.open;
485
406
  let config = view.state.facet(completionConfig);
407
+ this.optionContent = optionContent(config);
408
+ this.optionClass = config.optionClass;
486
409
  this.range = rangeAroundSelected(options.length, selected, config.maxRenderedOptions);
487
410
  this.dom = document.createElement("div");
488
411
  this.dom.className = "cm-tooltip-autocomplete";
@@ -495,7 +418,7 @@ class CompletionTooltip {
495
418
  }
496
419
  }
497
420
  });
498
- this.list = this.dom.appendChild(createListBox(options, cState.id, this.range));
421
+ this.list = this.dom.appendChild(this.createListBox(options, cState.id, this.range));
499
422
  this.list.addEventListener("scroll", () => {
500
423
  if (this.info)
501
424
  this.view.requestMeasure(this.placeInfo);
@@ -515,7 +438,7 @@ class CompletionTooltip {
515
438
  if (open.selected < this.range.from || open.selected >= this.range.to) {
516
439
  this.range = rangeAroundSelected(open.options.length, open.selected, this.view.state.facet(completionConfig).maxRenderedOptions);
517
440
  this.list.remove();
518
- this.list = this.dom.appendChild(createListBox(open.options, cState.id, this.range));
441
+ this.list = this.dom.appendChild(this.createListBox(open.options, cState.id, this.range));
519
442
  this.list.addEventListener("scroll", () => {
520
443
  if (this.info)
521
444
  this.view.requestMeasure(this.placeInfo);
@@ -553,17 +476,17 @@ class CompletionTooltip {
553
476
  }
554
477
  measureInfo() {
555
478
  let sel = this.dom.querySelector("[aria-selected]");
556
- if (!sel)
479
+ if (!sel || !this.info)
557
480
  return null;
558
- let rect = this.dom.getBoundingClientRect();
559
- let top = sel.getBoundingClientRect().top - rect.top;
481
+ let rect = this.dom.getBoundingClientRect(), infoRect = this.info.getBoundingClientRect();
482
+ let top = Math.min(sel.getBoundingClientRect().top, innerHeight - infoRect.height) - rect.top;
560
483
  if (top < 0 || top > this.list.clientHeight - 10)
561
484
  return null;
562
485
  let left = this.view.textDirection == Direction.RTL;
563
486
  let spaceLeft = rect.left, spaceRight = innerWidth - rect.right;
564
- if (left && spaceLeft < Math.min(MaxInfoWidth, spaceRight))
487
+ if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
565
488
  left = false;
566
- else if (!left && spaceRight < Math.min(MaxInfoWidth, spaceLeft))
489
+ else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
567
490
  left = true;
568
491
  return { top, left };
569
492
  }
@@ -574,6 +497,30 @@ class CompletionTooltip {
574
497
  this.info.classList.toggle("cm-completionInfo-right", !pos.left);
575
498
  }
576
499
  }
500
+ createListBox(options, id, range) {
501
+ const ul = document.createElement("ul");
502
+ ul.id = id;
503
+ ul.setAttribute("role", "listbox");
504
+ for (let i = range.from; i < range.to; i++) {
505
+ let { completion, match } = options[i];
506
+ const li = ul.appendChild(document.createElement("li"));
507
+ li.id = id + "-" + i;
508
+ li.setAttribute("role", "option");
509
+ let cls = this.optionClass(completion);
510
+ if (cls)
511
+ li.className = cls;
512
+ for (let source of this.optionContent) {
513
+ let node = source(completion, this.view.state, match);
514
+ if (node)
515
+ li.appendChild(node);
516
+ }
517
+ }
518
+ if (range.from)
519
+ ul.classList.add("cm-completionListIncompleteTop");
520
+ if (range.to < options.length)
521
+ ul.classList.add("cm-completionListIncompleteBottom");
522
+ return ul;
523
+ }
577
524
  }
578
525
  // We allocate a new function instance every time the completion
579
526
  // changes to force redrawing/repositioning of the tooltip
@@ -708,20 +655,24 @@ function sameResults(a, b) {
708
655
  return false;
709
656
  }
710
657
  }
658
+ const baseAttrs = {
659
+ "aria-autocomplete": "list",
660
+ "aria-expanded": "false"
661
+ };
711
662
  function makeAttrs(id, selected) {
712
663
  return {
713
664
  "aria-autocomplete": "list",
665
+ "aria-expanded": "true",
714
666
  "aria-activedescendant": id + "-" + selected,
715
- "aria-owns": id
667
+ "aria-controls": id
716
668
  };
717
669
  }
718
- const baseAttrs = { "aria-autocomplete": "list" }, none = [];
670
+ const none = [];
719
671
  function cmpOption(a, b) {
720
672
  let dScore = b.match[0] - a.match[0];
721
673
  if (dScore)
722
674
  return dScore;
723
- let lA = a.completion.label, lB = b.completion.label;
724
- return lA < lB ? -1 : lA == lB ? 0 : 1;
675
+ return a.completion.label.localeCompare(b.completion.label);
725
676
  }
726
677
  function getUserEvent(tr) {
727
678
  return tr.isUserEvent("input.type") ? "input" : tr.isUserEvent("delete.backward") ? "delete" : null;
@@ -832,7 +783,7 @@ Accept the current completion.
832
783
  */
833
784
  const acceptCompletion = (view) => {
834
785
  let cState = view.state.field(completionState, false);
835
- if (!cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin)
786
+ if (view.state.readOnly || !cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin)
836
787
  return false;
837
788
  applyCompletion(view, cState.open.options[cState.open.selected]);
838
789
  return true;
@@ -1008,6 +959,106 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
1008
959
  }
1009
960
  });
1010
961
 
962
+ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
963
+ ".cm-tooltip.cm-tooltip-autocomplete": {
964
+ "& > ul": {
965
+ fontFamily: "monospace",
966
+ whiteSpace: "nowrap",
967
+ overflow: "auto",
968
+ maxWidth_fallback: "700px",
969
+ maxWidth: "min(700px, 95vw)",
970
+ maxHeight: "10em",
971
+ listStyle: "none",
972
+ margin: 0,
973
+ padding: 0,
974
+ "& > li": {
975
+ cursor: "pointer",
976
+ padding: "1px 1em 1px 3px",
977
+ lineHeight: 1.2
978
+ },
979
+ }
980
+ },
981
+ "&light .cm-tooltip-autocomplete ul li[aria-selected]": {
982
+ background: "#39e",
983
+ color: "white",
984
+ },
985
+ "&dark .cm-tooltip-autocomplete ul li[aria-selected]": {
986
+ background: "#347",
987
+ color: "white",
988
+ },
989
+ ".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after": {
990
+ content: '"···"',
991
+ opacity: 0.5,
992
+ display: "block",
993
+ textAlign: "center"
994
+ },
995
+ ".cm-tooltip.cm-completionInfo": {
996
+ position: "absolute",
997
+ padding: "3px 9px",
998
+ width: "max-content",
999
+ maxWidth: "300px",
1000
+ },
1001
+ ".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
1002
+ ".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
1003
+ "&light .cm-snippetField": { backgroundColor: "#00000022" },
1004
+ "&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
1005
+ ".cm-snippetFieldPosition": {
1006
+ verticalAlign: "text-top",
1007
+ width: 0,
1008
+ height: "1.15em",
1009
+ margin: "0 -0.7px -.7em",
1010
+ borderLeft: "1.4px dotted #888"
1011
+ },
1012
+ ".cm-completionMatchedText": {
1013
+ textDecoration: "underline"
1014
+ },
1015
+ ".cm-completionDetail": {
1016
+ marginLeft: "0.5em",
1017
+ fontStyle: "italic"
1018
+ },
1019
+ ".cm-completionIcon": {
1020
+ fontSize: "90%",
1021
+ width: ".8em",
1022
+ display: "inline-block",
1023
+ textAlign: "center",
1024
+ paddingRight: ".6em",
1025
+ opacity: "0.6"
1026
+ },
1027
+ ".cm-completionIcon-function, .cm-completionIcon-method": {
1028
+ "&:after": { content: "'ƒ'" }
1029
+ },
1030
+ ".cm-completionIcon-class": {
1031
+ "&:after": { content: "'○'" }
1032
+ },
1033
+ ".cm-completionIcon-interface": {
1034
+ "&:after": { content: "'◌'" }
1035
+ },
1036
+ ".cm-completionIcon-variable": {
1037
+ "&:after": { content: "'𝑥'" }
1038
+ },
1039
+ ".cm-completionIcon-constant": {
1040
+ "&:after": { content: "'𝐶'" }
1041
+ },
1042
+ ".cm-completionIcon-type": {
1043
+ "&:after": { content: "'𝑡'" }
1044
+ },
1045
+ ".cm-completionIcon-enum": {
1046
+ "&:after": { content: "'∪'" }
1047
+ },
1048
+ ".cm-completionIcon-property": {
1049
+ "&:after": { content: "'□'" }
1050
+ },
1051
+ ".cm-completionIcon-keyword": {
1052
+ "&:after": { content: "'🔑\uFE0E'" } // Disable emoji rendering
1053
+ },
1054
+ ".cm-completionIcon-namespace": {
1055
+ "&:after": { content: "'▢'" }
1056
+ },
1057
+ ".cm-completionIcon-text": {
1058
+ "&:after": { content: "'abc'", fontSize: "50%", verticalAlign: "middle" }
1059
+ }
1060
+ });
1061
+
1011
1062
  class FieldPos {
1012
1063
  constructor(field, line, from, to) {
1013
1064
  this.field = field;
@@ -1158,8 +1209,7 @@ function snippet(template) {
1158
1209
  let active = new ActiveSnippet(ranges, 0);
1159
1210
  let effects = spec.effects = [setActive.of(active)];
1160
1211
  if (editor.state.field(snippetState, false) === undefined)
1161
- effects.push(StateEffect.appendConfig.of([snippetState.init(() => active), addSnippetKeymap,
1162
- snippetPointerHandler, baseTheme]));
1212
+ effects.push(StateEffect.appendConfig.of([snippetState, addSnippetKeymap, snippetPointerHandler, baseTheme]));
1163
1213
  }
1164
1214
  editor.dispatch(editor.state.update(spec));
1165
1215
  };
@@ -1209,7 +1259,7 @@ to [`clearSnippet`](https://codemirror.net/6/docs/ref/#autocomplete.clearSnippet
1209
1259
  const snippetKeymap = /*@__PURE__*/Facet.define({
1210
1260
  combine(maps) { return maps.length ? maps[0] : defaultSnippetKeymap; }
1211
1261
  });
1212
- const addSnippetKeymap = /*@__PURE__*/Prec.override(/*@__PURE__*/keymap.compute([snippetKeymap], state => state.facet(snippetKeymap)));
1262
+ const addSnippetKeymap = /*@__PURE__*/Prec.highest(/*@__PURE__*/keymap.compute([snippetKeymap], state => state.facet(snippetKeymap)));
1213
1263
  /**
1214
1264
  Create a completion from a snippet. Returns an object with the
1215
1265
  properties from `completion`, plus an `apply` function that
@@ -1234,42 +1284,81 @@ const snippetPointerHandler = /*@__PURE__*/EditorView.domEventHandlers({
1234
1284
  }
1235
1285
  });
1236
1286
 
1237
- /**
1238
- A completion source that will scan the document for words (using a
1239
- [character categorizer](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer)), and
1240
- return those as completions.
1241
- */
1242
- const completeAnyWord = context => {
1243
- let options = [], seen = Object.create(null);
1244
- let cat = context.state.charCategorizer(context.pos);
1245
- let start = Math.max(0, context.pos - 50000 /* Range */), end = Math.min(context.state.doc.length, start + 50000 /* Range */ * 2);
1246
- let from = context.pos;
1247
- for (let cur = context.state.doc.iterRange(start, end), pos = start; !(cur.next()).done;) {
1248
- let { value } = cur, start = -1;
1249
- for (let i = 0;; i++) {
1250
- if (i < value.length && cat(value[i]) == CharCategory.Word) {
1251
- if (start < 0)
1252
- start = i;
1287
+ function wordRE(wordChars) {
1288
+ let escaped = wordChars.replace(/[\\[.+*?(){|^$]/g, "\\$&");
1289
+ try {
1290
+ return new RegExp(`[\\p{Alphabetic}\\p{Number}_${escaped}]+`, "ug");
1291
+ }
1292
+ catch (_a) {
1293
+ return new RegExp(`[\w${escaped}]`, "g");
1294
+ }
1295
+ }
1296
+ function mapRE(re, f) {
1297
+ return new RegExp(f(re.source), re.unicode ? "u" : "");
1298
+ }
1299
+ const wordCaches = /*@__PURE__*/Object.create(null);
1300
+ function wordCache(wordChars) {
1301
+ return wordCaches[wordChars] || (wordCaches[wordChars] = new WeakMap);
1302
+ }
1303
+ function storeWords(doc, wordRE, result, seen, ignoreAt) {
1304
+ for (let lines = doc.iterLines(), pos = 0; !lines.next().done;) {
1305
+ let { value } = lines, m;
1306
+ wordRE.lastIndex = 0;
1307
+ while (m = wordRE.exec(value)) {
1308
+ if (!seen[m[0]] && pos + m.index != ignoreAt) {
1309
+ result.push({ type: "text", label: m[0] });
1310
+ seen[m[0]] = true;
1311
+ if (result.length >= 2000 /* MaxList */)
1312
+ return;
1253
1313
  }
1254
- else if (start > -1) {
1255
- if (pos + start <= context.pos && pos + i >= context.pos) {
1256
- from = pos + start;
1257
- }
1258
- else {
1259
- let word = value.slice(start, i);
1260
- if (!seen[word]) {
1261
- options.push({ type: "text", label: word });
1262
- seen[word] = true;
1314
+ }
1315
+ pos += value.length + 1;
1316
+ }
1317
+ }
1318
+ function collectWords(doc, cache, wordRE, to, ignoreAt) {
1319
+ let big = doc.length >= 1000 /* MinCacheLen */;
1320
+ let cached = big && cache.get(doc);
1321
+ if (cached)
1322
+ return cached;
1323
+ let result = [], seen = Object.create(null);
1324
+ if (doc.children) {
1325
+ let pos = 0;
1326
+ for (let ch of doc.children) {
1327
+ if (ch.length >= 1000 /* MinCacheLen */) {
1328
+ for (let c of collectWords(ch, cache, wordRE, to - pos, ignoreAt - pos)) {
1329
+ if (!seen[c.label]) {
1330
+ seen[c.label] = true;
1331
+ result.push(c);
1263
1332
  }
1264
1333
  }
1265
- start = -1;
1266
1334
  }
1267
- if (i == value.length)
1268
- break;
1335
+ else {
1336
+ storeWords(ch, wordRE, result, seen, ignoreAt - pos);
1337
+ }
1338
+ pos += ch.length + 1;
1269
1339
  }
1270
- pos += value.length;
1271
1340
  }
1272
- return { from, options, span: /^\w*/ };
1341
+ else {
1342
+ storeWords(doc, wordRE, result, seen, ignoreAt);
1343
+ }
1344
+ if (big && result.length < 2000 /* MaxList */)
1345
+ cache.set(doc, result);
1346
+ return result;
1347
+ }
1348
+ /**
1349
+ A completion source that will scan the document for words (using a
1350
+ [character categorizer](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer)), and
1351
+ return those as completions.
1352
+ */
1353
+ const completeAnyWord = context => {
1354
+ let wordChars = context.state.languageDataAt("wordChars", context.pos).join("");
1355
+ let re = wordRE(wordChars);
1356
+ let token = context.matchBefore(mapRE(re, s => s + "$"));
1357
+ if (!token && !context.explicit)
1358
+ return null;
1359
+ let from = token ? token.from : context.pos;
1360
+ let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* Range */, from);
1361
+ return { from, options, span: mapRE(re, s => "^" + s) };
1273
1362
  };
1274
1363
 
1275
1364
  /**
@@ -1304,7 +1393,7 @@ const completionKeymap = [
1304
1393
  { key: "PageUp", run: /*@__PURE__*/moveCompletionSelection(false, "page") },
1305
1394
  { key: "Enter", run: acceptCompletion }
1306
1395
  ];
1307
- const completionKeymapExt = /*@__PURE__*/Prec.override(/*@__PURE__*/keymap.computeN([completionConfig], state => state.facet(completionConfig).defaultKeymap ? [completionKeymap] : []));
1396
+ const completionKeymapExt = /*@__PURE__*/Prec.highest(/*@__PURE__*/keymap.computeN([completionConfig], state => state.facet(completionConfig).defaultKeymap ? [completionKeymap] : []));
1308
1397
  /**
1309
1398
  Get the current completion status. When completions are available,
1310
1399
  this will return `"active"`. When completions are pending (in the
@@ -1324,5 +1413,13 @@ function currentCompletions(state) {
1324
1413
  let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
1325
1414
  return open ? open.options.map(o => o.completion) : [];
1326
1415
  }
1416
+ /**
1417
+ Return the currently selected completion, if any.
1418
+ */
1419
+ function selectedCompletion(state) {
1420
+ var _a;
1421
+ let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
1422
+ return open ? open.options[open.selected].completion : null;
1423
+ }
1327
1424
 
1328
- export { CompletionContext, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifIn, ifNotIn, moveCompletionSelection, nextSnippetField, prevSnippetField, snippet, snippetCompletion, snippetKeymap, startCompletion };
1425
+ export { CompletionContext, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifIn, ifNotIn, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/autocomplete",
3
- "version": "0.19.1",
3
+ "version": "0.19.5",
4
4
  "description": "Autocompletion for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -27,8 +27,8 @@
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
29
  "@codemirror/language": "^0.19.0",
30
- "@codemirror/state": "^0.19.0",
31
- "@codemirror/text": "^0.19.0",
30
+ "@codemirror/state": "^0.19.4",
31
+ "@codemirror/text": "^0.19.2",
32
32
  "@codemirror/tooltip": "^0.19.0",
33
33
  "@codemirror/view": "^0.19.0",
34
34
  "@lezer/common": "^0.15.0"