@codemirror/autocomplete 0.19.14 → 0.20.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/dist/index.js CHANGED
@@ -1,8 +1,6 @@
1
- import { Annotation, Facet, combineConfig, StateEffect, StateField, Prec, EditorSelection, Text, MapMode } from '@codemirror/state';
2
- import { Direction, logException, EditorView, ViewPlugin, Decoration, WidgetType, keymap } from '@codemirror/view';
3
- import { showTooltip, getTooltip } from '@codemirror/tooltip';
1
+ import { Annotation, EditorSelection, codePointAt, codePointSize, fromCodePoint, Facet, combineConfig, StateEffect, StateField, Prec, Text, MapMode, RangeValue, RangeSet, CharCategory } from '@codemirror/state';
2
+ import { logException, Direction, showTooltip, EditorView, ViewPlugin, getTooltip, Decoration, WidgetType, keymap } from '@codemirror/view';
4
3
  import { syntaxTree, indentUnit } from '@codemirror/language';
5
- import { codePointAt, codePointSize, fromCodePoint } from '@codemirror/text';
6
4
 
7
5
  /**
8
6
  An instance of this is passed to completion source functions.
@@ -98,10 +96,10 @@ completes them.
98
96
  */
99
97
  function completeFromList(list) {
100
98
  let options = list.map(o => typeof o == "string" ? { label: o } : o);
101
- let [span, match] = options.every(o => /^\w+$/.test(o.label)) ? [/\w*$/, /\w+$/] : prefixMatch(options);
99
+ let [validFor, match] = options.every(o => /^\w+$/.test(o.label)) ? [/\w*$/, /\w+$/] : prefixMatch(options);
102
100
  return (context) => {
103
101
  let token = context.matchBefore(match);
104
- return token || context.explicit ? { from: token ? token.from : context.pos, options, span } : null;
102
+ return token || context.explicit ? { from: token ? token.from : context.pos, options, validFor } : null;
105
103
  };
106
104
  }
107
105
  /**
@@ -152,12 +150,24 @@ picking a completion.
152
150
  */
153
151
  const pickedCompletion = /*@__PURE__*/Annotation.define();
154
152
  function applyCompletion(view, option) {
155
- let apply = option.completion.apply || option.completion.label;
153
+ const apply = option.completion.apply || option.completion.label;
156
154
  let result = option.source;
157
155
  if (typeof apply == "string") {
158
- view.dispatch({
159
- changes: { from: result.from, to: result.to, insert: apply },
160
- selection: { anchor: result.from + apply.length },
156
+ view.dispatch(view.state.changeByRange(range => {
157
+ if (range == view.state.selection.main)
158
+ return {
159
+ changes: { from: result.from, to: result.to, insert: apply },
160
+ range: EditorSelection.cursor(result.from + apply.length)
161
+ };
162
+ let len = result.to - result.from;
163
+ if (!range.empty ||
164
+ len && view.state.sliceDoc(range.from - len, range.from) != view.state.sliceDoc(result.from, result.to))
165
+ return { range };
166
+ return {
167
+ changes: { from: range.from - len, to: range.from, insert: apply },
168
+ range: EditorSelection.cursor(range.from - len + apply.length)
169
+ };
170
+ }), {
161
171
  userEvent: "input.complete",
162
172
  annotations: pickedCompletion.of(option.completion)
163
173
  });
@@ -306,6 +316,7 @@ const completionConfig = /*@__PURE__*/Facet.define({
306
316
  return combineConfig(configs, {
307
317
  activateOnTyping: true,
308
318
  override: null,
319
+ closeOnBlur: true,
309
320
  maxRenderedOptions: 100,
310
321
  defaultKeymap: true,
311
322
  optionClass: () => "",
@@ -314,6 +325,7 @@ const completionConfig = /*@__PURE__*/Facet.define({
314
325
  addToOptions: []
315
326
  }, {
316
327
  defaultKeymap: (a, b) => a && b,
328
+ closeOnBlur: (a, b) => a && b,
317
329
  icons: (a, b) => a && b,
318
330
  optionClass: (a, b) => c => joinClass(a(c), b(c)),
319
331
  addToOptions: (a, b) => a.concat(b)
@@ -370,22 +382,6 @@ function optionContent(config) {
370
382
  });
371
383
  return content.sort((a, b) => a.position - b.position).map(a => a.render);
372
384
  }
373
- function createInfoDialog(option, view) {
374
- let dom = document.createElement("div");
375
- dom.className = "cm-tooltip cm-completionInfo";
376
- let { info } = option.completion;
377
- if (typeof info == "string") {
378
- dom.textContent = info;
379
- }
380
- else {
381
- let content = info(option.completion);
382
- if (content.then)
383
- content.then(node => dom.appendChild(node), e => logException(view.state, e, "completion info"));
384
- else
385
- dom.appendChild(content);
386
- }
387
- return dom;
388
- }
389
385
  function rangeAroundSelected(total, selected, max) {
390
386
  if (total <= max)
391
387
  return { from: 0, to: total };
@@ -454,13 +450,31 @@ class CompletionTooltip {
454
450
  this.info.remove();
455
451
  this.info = null;
456
452
  }
457
- let option = open.options[open.selected];
458
- if (option.completion.info) {
459
- this.info = this.dom.appendChild(createInfoDialog(option, this.view));
460
- this.view.requestMeasure(this.placeInfo);
453
+ let { completion } = open.options[open.selected];
454
+ let { info } = completion;
455
+ if (!info)
456
+ return;
457
+ let infoResult = typeof info === 'string' ? document.createTextNode(info) : info(completion);
458
+ if (!infoResult)
459
+ return;
460
+ if ('then' in infoResult) {
461
+ infoResult.then(node => {
462
+ if (node && this.view.state.field(this.stateField, false) == cState)
463
+ this.addInfoPane(node);
464
+ }).catch(e => logException(this.view.state, e, "completion info"));
465
+ }
466
+ else {
467
+ this.addInfoPane(infoResult);
461
468
  }
462
469
  }
463
470
  }
471
+ addInfoPane(content) {
472
+ let dom = this.info = document.createElement("div");
473
+ dom.className = "cm-tooltip cm-completionInfo";
474
+ dom.appendChild(content);
475
+ this.dom.appendChild(dom);
476
+ this.view.requestMeasure(this.placeInfo);
477
+ }
464
478
  updateSelectedOption(selected) {
465
479
  let set = null;
466
480
  for (let opt = this.list.firstChild, i = this.range.from; opt; opt = opt.nextSibling, i++) {
@@ -546,7 +560,6 @@ function scrollIntoView(container, element) {
546
560
  container.scrollTop += self.bottom - parent.bottom;
547
561
  }
548
562
 
549
- const MaxOptions = 300;
550
563
  // Used to pick a preferred option when two options with the same
551
564
  // label occur in the result.
552
565
  function score(option) {
@@ -558,8 +571,14 @@ function sortOptions(active, state) {
558
571
  for (let a of active)
559
572
  if (a.hasResult()) {
560
573
  if (a.result.filter === false) {
561
- for (let option of a.result.options)
562
- options.push(new Option(option, a, [1e9 - i++]));
574
+ let getMatch = a.result.getMatch;
575
+ for (let option of a.result.options) {
576
+ let match = [1e9 - i++];
577
+ if (getMatch)
578
+ for (let n of getMatch(option))
579
+ match.push(n);
580
+ options.push(new Option(option, a, match));
581
+ }
563
582
  }
564
583
  else {
565
584
  let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match;
@@ -573,10 +592,9 @@ function sortOptions(active, state) {
573
592
  }
574
593
  let result = [], prev = null;
575
594
  for (let opt of options.sort(cmpOption)) {
576
- if (result.length == MaxOptions)
577
- break;
578
595
  if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail ||
579
- prev.type != opt.completion.type || prev.apply != opt.completion.apply)
596
+ (prev.type != null && opt.completion.type != null && prev.type != opt.completion.type) ||
597
+ prev.apply != opt.completion.apply)
580
598
  result.push(opt);
581
599
  else if (score(opt.completion) > score(prev))
582
600
  result[result.length - 1] = opt;
@@ -726,24 +744,27 @@ class ActiveSource {
726
744
  }
727
745
  }
728
746
  class ActiveResult extends ActiveSource {
729
- constructor(source, explicitPos, result, from, to, span) {
747
+ constructor(source, explicitPos, result, from, to) {
730
748
  super(source, 2 /* Result */, explicitPos);
731
749
  this.result = result;
732
750
  this.from = from;
733
751
  this.to = to;
734
- this.span = span;
735
752
  }
736
753
  hasResult() { return true; }
737
754
  handleUserEvent(tr, type, conf) {
755
+ var _a;
738
756
  let from = tr.changes.mapPos(this.from), to = tr.changes.mapPos(this.to, 1);
739
757
  let pos = cur(tr.state);
740
758
  if ((this.explicitPos < 0 ? pos <= from : pos < this.from) ||
741
759
  pos > to ||
742
760
  type == "delete" && cur(tr.startState) == this.from)
743
761
  return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */);
744
- let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos);
745
- if (this.span && (from == to || this.span.test(tr.state.sliceDoc(from, to))))
746
- return new ActiveResult(this.source, explicitPos, this.result, from, to, this.span);
762
+ let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos), updated;
763
+ if (checkValid(this.result.validFor, tr.state, from, to))
764
+ return new ActiveResult(this.source, explicitPos, this.result, from, to);
765
+ if (this.result.update &&
766
+ (updated = this.result.update(this.result, from, to, new CompletionContext(tr.state, pos, explicitPos >= 0))))
767
+ return new ActiveResult(this.source, explicitPos, updated, updated.from, (_a = updated.to) !== null && _a !== void 0 ? _a : cur(tr.state));
747
768
  return new ActiveSource(this.source, 1 /* Pending */, explicitPos);
748
769
  }
749
770
  handleChange(tr) {
@@ -751,9 +772,15 @@ class ActiveResult extends ActiveSource {
751
772
  }
752
773
  map(mapping) {
753
774
  return mapping.empty ? this :
754
- new ActiveResult(this.source, this.explicitPos < 0 ? -1 : mapping.mapPos(this.explicitPos), this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1), this.span);
775
+ new ActiveResult(this.source, this.explicitPos < 0 ? -1 : mapping.mapPos(this.explicitPos), this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1));
755
776
  }
756
777
  }
778
+ function checkValid(validFor, state, from, to) {
779
+ if (!validFor)
780
+ return false;
781
+ let text = state.sliceDoc(from, to);
782
+ return typeof validFor == "function" ? validFor(text, from, to, state) : ensureAnchor(validFor, true).test(text);
783
+ }
757
784
  const startCompletionEffect = /*@__PURE__*/StateEffect.define();
758
785
  const closeCompletionEffect = /*@__PURE__*/StateEffect.define();
759
786
  const setActiveEffect = /*@__PURE__*/StateEffect.define({
@@ -927,7 +954,7 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
927
954
  continue;
928
955
  this.running.splice(i--, 1);
929
956
  if (query.done) {
930
- let active = new ActiveResult(query.active.source, query.active.explicitPos, query.done, query.done.from, (_a = query.done.to) !== null && _a !== void 0 ? _a : cur(query.updates.length ? query.updates[0].startState : this.view.state), query.done.span && query.done.filter !== false ? ensureAnchor(query.done.span, true) : null);
957
+ let active = new ActiveResult(query.active.source, query.active.explicitPos, query.done, query.done.from, (_a = query.done.to) !== null && _a !== void 0 ? _a : cur(query.updates.length ? query.updates[0].startState : this.view.state));
931
958
  // Replay the transactions that happened since the start of
932
959
  // the request and see if that preserves the result
933
960
  for (let tr of query.updates)
@@ -959,6 +986,11 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
959
986
  }
960
987
  }, {
961
988
  eventHandlers: {
989
+ blur() {
990
+ let state = this.view.state.field(completionState, false);
991
+ if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur)
992
+ this.view.dispatch({ effects: closeCompletionEffect.of(null) });
993
+ },
962
994
  compositionstart() {
963
995
  this.composing = 1 /* Started */;
964
996
  },
@@ -1200,8 +1232,9 @@ function fieldSelection(ranges, field) {
1200
1232
  return EditorSelection.create(ranges.filter(r => r.field == field).map(r => EditorSelection.range(r.from, r.to)));
1201
1233
  }
1202
1234
  /**
1203
- Convert a snippet template to a function that can apply it.
1204
- Snippets are written using syntax like this:
1235
+ Convert a snippet template to a function that can
1236
+ [apply](https://codemirror.net/6/docs/ref/#autocomplete.Completion.apply) it. Snippets are written
1237
+ using syntax like this:
1205
1238
 
1206
1239
  "for (let ${index} = 0; ${index} < ${end}; ${index}++) {\n\t${}\n}"
1207
1240
 
@@ -1384,8 +1417,238 @@ const completeAnyWord = context => {
1384
1417
  return null;
1385
1418
  let from = token ? token.from : context.pos;
1386
1419
  let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* Range */, from);
1387
- return { from, options, span: mapRE(re, s => "^" + s) };
1420
+ return { from, options, validFor: mapRE(re, s => "^" + s) };
1421
+ };
1422
+
1423
+ const defaults = {
1424
+ brackets: ["(", "[", "{", "'", '"'],
1425
+ before: ")]}:;>"
1426
+ };
1427
+ const closeBracketEffect = /*@__PURE__*/StateEffect.define({
1428
+ map(value, mapping) {
1429
+ let mapped = mapping.mapPos(value, -1, MapMode.TrackAfter);
1430
+ return mapped == null ? undefined : mapped;
1431
+ }
1432
+ });
1433
+ const skipBracketEffect = /*@__PURE__*/StateEffect.define({
1434
+ map(value, mapping) { return mapping.mapPos(value); }
1435
+ });
1436
+ const closedBracket = /*@__PURE__*/new class extends RangeValue {
1437
+ };
1438
+ closedBracket.startSide = 1;
1439
+ closedBracket.endSide = -1;
1440
+ const bracketState = /*@__PURE__*/StateField.define({
1441
+ create() { return RangeSet.empty; },
1442
+ update(value, tr) {
1443
+ if (tr.selection) {
1444
+ let lineStart = tr.state.doc.lineAt(tr.selection.main.head).from;
1445
+ let prevLineStart = tr.startState.doc.lineAt(tr.startState.selection.main.head).from;
1446
+ if (lineStart != tr.changes.mapPos(prevLineStart, -1))
1447
+ value = RangeSet.empty;
1448
+ }
1449
+ value = value.map(tr.changes);
1450
+ for (let effect of tr.effects) {
1451
+ if (effect.is(closeBracketEffect))
1452
+ value = value.update({ add: [closedBracket.range(effect.value, effect.value + 1)] });
1453
+ else if (effect.is(skipBracketEffect))
1454
+ value = value.update({ filter: from => from != effect.value });
1455
+ }
1456
+ return value;
1457
+ }
1458
+ });
1459
+ /**
1460
+ Extension to enable bracket-closing behavior. When a closeable
1461
+ bracket is typed, its closing bracket is immediately inserted
1462
+ after the cursor. When closing a bracket directly in front of a
1463
+ closing bracket inserted by the extension, the cursor moves over
1464
+ that bracket.
1465
+ */
1466
+ function closeBrackets() {
1467
+ return [inputHandler, bracketState];
1468
+ }
1469
+ const definedClosing = "()[]{}<>";
1470
+ function closing(ch) {
1471
+ for (let i = 0; i < definedClosing.length; i += 2)
1472
+ if (definedClosing.charCodeAt(i) == ch)
1473
+ return definedClosing.charAt(i + 1);
1474
+ return fromCodePoint(ch < 128 ? ch : ch + 1);
1475
+ }
1476
+ function config(state, pos) {
1477
+ return state.languageDataAt("closeBrackets", pos)[0] || defaults;
1478
+ }
1479
+ const android = typeof navigator == "object" && /*@__PURE__*//Android\b/.test(navigator.userAgent);
1480
+ const inputHandler = /*@__PURE__*/EditorView.inputHandler.of((view, from, to, insert) => {
1481
+ if ((android ? view.composing : view.compositionStarted) || view.state.readOnly)
1482
+ return false;
1483
+ let sel = view.state.selection.main;
1484
+ if (insert.length > 2 || insert.length == 2 && codePointSize(codePointAt(insert, 0)) == 1 ||
1485
+ from != sel.from || to != sel.to)
1486
+ return false;
1487
+ let tr = insertBracket(view.state, insert);
1488
+ if (!tr)
1489
+ return false;
1490
+ view.dispatch(tr);
1491
+ return true;
1492
+ });
1493
+ /**
1494
+ Command that implements deleting a pair of matching brackets when
1495
+ the cursor is between them.
1496
+ */
1497
+ const deleteBracketPair = ({ state, dispatch }) => {
1498
+ if (state.readOnly)
1499
+ return false;
1500
+ let conf = config(state, state.selection.main.head);
1501
+ let tokens = conf.brackets || defaults.brackets;
1502
+ let dont = null, changes = state.changeByRange(range => {
1503
+ if (range.empty) {
1504
+ let before = prevChar(state.doc, range.head);
1505
+ for (let token of tokens) {
1506
+ if (token == before && nextChar(state.doc, range.head) == closing(codePointAt(token, 0)))
1507
+ return { changes: { from: range.head - token.length, to: range.head + token.length },
1508
+ range: EditorSelection.cursor(range.head - token.length),
1509
+ userEvent: "delete.backward" };
1510
+ }
1511
+ }
1512
+ return { range: dont = range };
1513
+ });
1514
+ if (!dont)
1515
+ dispatch(state.update(changes, { scrollIntoView: true }));
1516
+ return !dont;
1388
1517
  };
1518
+ /**
1519
+ Close-brackets related key bindings. Binds Backspace to
1520
+ [`deleteBracketPair`](https://codemirror.net/6/docs/ref/#autocomplete.deleteBracketPair).
1521
+ */
1522
+ const closeBracketsKeymap = [
1523
+ { key: "Backspace", run: deleteBracketPair }
1524
+ ];
1525
+ /**
1526
+ Implements the extension's behavior on text insertion. If the
1527
+ given string counts as a bracket in the language around the
1528
+ selection, and replacing the selection with it requires custom
1529
+ behavior (inserting a closing version or skipping past a
1530
+ previously-closed bracket), this function returns a transaction
1531
+ representing that custom behavior. (You only need this if you want
1532
+ to programmatically insert brackets—the
1533
+ [`closeBrackets`](https://codemirror.net/6/docs/ref/#autocomplete.closeBrackets) extension will
1534
+ take care of running this for user input.)
1535
+ */
1536
+ function insertBracket(state, bracket) {
1537
+ let conf = config(state, state.selection.main.head);
1538
+ let tokens = conf.brackets || defaults.brackets;
1539
+ for (let tok of tokens) {
1540
+ let closed = closing(codePointAt(tok, 0));
1541
+ if (bracket == tok)
1542
+ return closed == tok ? handleSame(state, tok, tokens.indexOf(tok + tok + tok) > -1)
1543
+ : handleOpen(state, tok, closed, conf.before || defaults.before);
1544
+ if (bracket == closed && closedBracketAt(state, state.selection.main.from))
1545
+ return handleClose(state, tok, closed);
1546
+ }
1547
+ return null;
1548
+ }
1549
+ function closedBracketAt(state, pos) {
1550
+ let found = false;
1551
+ state.field(bracketState).between(0, state.doc.length, from => {
1552
+ if (from == pos)
1553
+ found = true;
1554
+ });
1555
+ return found;
1556
+ }
1557
+ function nextChar(doc, pos) {
1558
+ let next = doc.sliceString(pos, pos + 2);
1559
+ return next.slice(0, codePointSize(codePointAt(next, 0)));
1560
+ }
1561
+ function prevChar(doc, pos) {
1562
+ let prev = doc.sliceString(pos - 2, pos);
1563
+ return codePointSize(codePointAt(prev, 0)) == prev.length ? prev : prev.slice(1);
1564
+ }
1565
+ function handleOpen(state, open, close, closeBefore) {
1566
+ let dont = null, changes = state.changeByRange(range => {
1567
+ if (!range.empty)
1568
+ return { changes: [{ insert: open, from: range.from }, { insert: close, from: range.to }],
1569
+ effects: closeBracketEffect.of(range.to + open.length),
1570
+ range: EditorSelection.range(range.anchor + open.length, range.head + open.length) };
1571
+ let next = nextChar(state.doc, range.head);
1572
+ if (!next || /\s/.test(next) || closeBefore.indexOf(next) > -1)
1573
+ return { changes: { insert: open + close, from: range.head },
1574
+ effects: closeBracketEffect.of(range.head + open.length),
1575
+ range: EditorSelection.cursor(range.head + open.length) };
1576
+ return { range: dont = range };
1577
+ });
1578
+ return dont ? null : state.update(changes, {
1579
+ scrollIntoView: true,
1580
+ userEvent: "input.type"
1581
+ });
1582
+ }
1583
+ function handleClose(state, _open, close) {
1584
+ let dont = null, moved = state.selection.ranges.map(range => {
1585
+ if (range.empty && nextChar(state.doc, range.head) == close)
1586
+ return EditorSelection.cursor(range.head + close.length);
1587
+ return dont = range;
1588
+ });
1589
+ return dont ? null : state.update({
1590
+ selection: EditorSelection.create(moved, state.selection.mainIndex),
1591
+ scrollIntoView: true,
1592
+ effects: state.selection.ranges.map(({ from }) => skipBracketEffect.of(from))
1593
+ });
1594
+ }
1595
+ // Handles cases where the open and close token are the same, and
1596
+ // possibly triple quotes (as in `"""abc"""`-style quoting).
1597
+ function handleSame(state, token, allowTriple) {
1598
+ let dont = null, changes = state.changeByRange(range => {
1599
+ if (!range.empty)
1600
+ return { changes: [{ insert: token, from: range.from }, { insert: token, from: range.to }],
1601
+ effects: closeBracketEffect.of(range.to + token.length),
1602
+ range: EditorSelection.range(range.anchor + token.length, range.head + token.length) };
1603
+ let pos = range.head, next = nextChar(state.doc, pos);
1604
+ if (next == token) {
1605
+ if (nodeStart(state, pos)) {
1606
+ return { changes: { insert: token + token, from: pos },
1607
+ effects: closeBracketEffect.of(pos + token.length),
1608
+ range: EditorSelection.cursor(pos + token.length) };
1609
+ }
1610
+ else if (closedBracketAt(state, pos)) {
1611
+ let isTriple = allowTriple && state.sliceDoc(pos, pos + token.length * 3) == token + token + token;
1612
+ return { range: EditorSelection.cursor(pos + token.length * (isTriple ? 3 : 1)),
1613
+ effects: skipBracketEffect.of(pos) };
1614
+ }
1615
+ }
1616
+ else if (allowTriple && state.sliceDoc(pos - 2 * token.length, pos) == token + token &&
1617
+ nodeStart(state, pos - 2 * token.length)) {
1618
+ return { changes: { insert: token + token + token + token, from: pos },
1619
+ effects: closeBracketEffect.of(pos + token.length),
1620
+ range: EditorSelection.cursor(pos + token.length) };
1621
+ }
1622
+ else if (state.charCategorizer(pos)(next) != CharCategory.Word) {
1623
+ let prev = state.sliceDoc(pos - 1, pos);
1624
+ if (prev != token && state.charCategorizer(pos)(prev) != CharCategory.Word && !probablyInString(state, pos, token))
1625
+ return { changes: { insert: token + token, from: pos },
1626
+ effects: closeBracketEffect.of(pos + token.length),
1627
+ range: EditorSelection.cursor(pos + token.length) };
1628
+ }
1629
+ return { range: dont = range };
1630
+ });
1631
+ return dont ? null : state.update(changes, {
1632
+ scrollIntoView: true,
1633
+ userEvent: "input.type"
1634
+ });
1635
+ }
1636
+ function nodeStart(state, pos) {
1637
+ let tree = syntaxTree(state).resolveInner(pos + 1);
1638
+ return tree.parent && tree.from == pos;
1639
+ }
1640
+ function probablyInString(state, pos, quoteToken) {
1641
+ let node = syntaxTree(state).resolveInner(pos, -1);
1642
+ for (let i = 0; i < 5; i++) {
1643
+ if (state.sliceDoc(node.from, node.from + quoteToken.length) == quoteToken)
1644
+ return true;
1645
+ let parent = node.to == pos && node.parent;
1646
+ if (!parent)
1647
+ break;
1648
+ node = parent;
1649
+ }
1650
+ return false;
1651
+ }
1389
1652
 
1390
1653
  /**
1391
1654
  Returns an extension that enables autocompletion.
@@ -1431,13 +1694,19 @@ function completionStatus(state) {
1431
1694
  return cState && cState.active.some(a => a.state == 1 /* Pending */) ? "pending"
1432
1695
  : cState && cState.active.some(a => a.state != 0 /* Inactive */) ? "active" : null;
1433
1696
  }
1697
+ const completionArrayCache = /*@__PURE__*/new WeakMap;
1434
1698
  /**
1435
1699
  Returns the available completions as an array.
1436
1700
  */
1437
1701
  function currentCompletions(state) {
1438
1702
  var _a;
1439
1703
  let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
1440
- return open ? open.options.map(o => o.completion) : [];
1704
+ if (!open)
1705
+ return [];
1706
+ let completions = completionArrayCache.get(open.options);
1707
+ if (!completions)
1708
+ completionArrayCache.set(open.options, completions = open.options.map(o => o.completion));
1709
+ return completions;
1441
1710
  }
1442
1711
  /**
1443
1712
  Return the currently selected completion, if any.
@@ -1447,5 +1716,21 @@ function selectedCompletion(state) {
1447
1716
  let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
1448
1717
  return open ? open.options[open.selected].completion : null;
1449
1718
  }
1719
+ /**
1720
+ Returns the currently selected position in the active completion
1721
+ list, or null if no completions are active.
1722
+ */
1723
+ function selectedCompletionIndex(state) {
1724
+ var _a;
1725
+ let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
1726
+ return open ? open.selected : null;
1727
+ }
1728
+ /**
1729
+ Create an effect that can be attached to a transaction to change
1730
+ the currently selected completion.
1731
+ */
1732
+ function setSelectedCompletion(index) {
1733
+ return setSelectedEffect.of(index);
1734
+ }
1450
1735
 
1451
- export { CompletionContext, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifIn, ifNotIn, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };
1736
+ export { CompletionContext, acceptCompletion, autocompletion, clearSnippet, closeBrackets, closeBracketsKeymap, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, deleteBracketPair, ifIn, ifNotIn, insertBracket, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, selectedCompletionIndex, setSelectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemirror/autocomplete",
3
- "version": "0.19.14",
3
+ "version": "0.20.1",
4
4
  "description": "Autocompletion for the CodeMirror code editor",
5
5
  "scripts": {
6
6
  "test": "cm-runtests",
@@ -26,12 +26,10 @@
26
26
  "sideEffects": false,
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
- "@codemirror/language": "^0.19.0",
30
- "@codemirror/state": "^0.19.4",
31
- "@codemirror/text": "^0.19.2",
32
- "@codemirror/tooltip": "^0.19.12",
33
- "@codemirror/view": "^0.19.0",
34
- "@lezer/common": "^0.15.0"
29
+ "@codemirror/language": "^0.20.0",
30
+ "@codemirror/state": "^0.20.0",
31
+ "@codemirror/view": "^0.20.0",
32
+ "@lezer/common": "^0.16.0"
35
33
  },
36
34
  "devDependencies": {
37
35
  "@codemirror/buildhelper": "^0.1.5"