@codemirror/autocomplete 0.18.4 → 0.18.8

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,35 @@
1
+ ## 0.18.8 (2021-06-30)
2
+
3
+ ### New features
4
+
5
+ Add an `ifIn` helper function that constrains a completion source to only fire when in a given syntax node. Add support for unfiltered completions
6
+
7
+ A completion result can now set a `filter: false` property to disable filtering and sorting of completions, when it already did so itself.
8
+
9
+ ## 0.18.7 (2021-06-14)
10
+
11
+ ### Bug fixes
12
+
13
+ Don't treat continued completions when typing after an explicit completion as explicit.
14
+
15
+ ## 0.18.6 (2021-06-03)
16
+
17
+ ### Bug fixes
18
+
19
+ Adding or reconfiguring completion sources will now cause them to be activated right away if a completion was active.
20
+
21
+ ### New features
22
+
23
+ You can now specify multiple types in `Completion.type` by separating them by spaces. Small doc comment tweak for Completion.type
24
+
25
+ ## 0.18.5 (2021-04-23)
26
+
27
+ ### Bug fixes
28
+
29
+ Fix a regression where snippet field selection didn't work with @codemirror/state 0.18.6.
30
+
31
+ Fix a bug where snippet fields with different position numbers were inappropriately merged.
32
+
1
33
  ## 0.18.4 (2021-04-20)
2
34
 
3
35
  ### Bug fixes
package/dist/index.cjs CHANGED
@@ -109,6 +109,18 @@ function completeFromList(list) {
109
109
  };
110
110
  }
111
111
  /**
112
+ Wrap the given completion source so that it will only fire when the
113
+ cursor is in a syntax node with one of the given names.
114
+ */
115
+ function ifIn(nodes, source) {
116
+ return (context) => {
117
+ for (let pos = language.syntaxTree(context.state).resolve(context.pos, -1); pos; pos = pos.parent)
118
+ if (nodes.indexOf(pos.name) > -1)
119
+ return source(context);
120
+ return null;
121
+ };
122
+ }
123
+ /**
112
124
  Wrap the given completion source so that it will not fire when the
113
125
  cursor is in a syntax node with one of the given names.
114
126
  */
@@ -406,7 +418,7 @@ function createListBox(options, id, range) {
406
418
  let icon = li.appendChild(document.createElement("div"));
407
419
  icon.classList.add("cm-completionIcon");
408
420
  if (completion.type)
409
- icon.classList.add("cm-completionIcon-" + completion.type);
421
+ icon.classList.add(...completion.type.split(/\s+/g).map(cls => "cm-completionIcon-" + cls));
410
422
  icon.setAttribute("aria-hidden", "true");
411
423
  let labelElt = li.appendChild(document.createElement("span"));
412
424
  labelElt.className = "cm-completionLabel";
@@ -588,16 +600,22 @@ function score(option) {
588
600
  (option.type ? 1 : 0);
589
601
  }
590
602
  function sortOptions(active, state) {
591
- let options = [];
603
+ let options = [], i = 0;
592
604
  for (let a of active)
593
605
  if (a.hasResult()) {
594
- let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match;
595
- for (let option of a.result.options)
596
- if (match = matcher.match(option.label)) {
597
- if (option.boost != null)
598
- match[0] += option.boost;
599
- options.push(new Option(option, a, match));
600
- }
606
+ if (a.result.filter === false) {
607
+ for (let option of a.result.options)
608
+ options.push(new Option(option, a, [1e9 - i++]));
609
+ }
610
+ else {
611
+ let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match;
612
+ for (let option of a.result.options)
613
+ if (match = matcher.match(option.label)) {
614
+ if (option.boost != null)
615
+ match[0] += option.boost;
616
+ options.push(new Option(option, a, match));
617
+ }
618
+ }
601
619
  }
602
620
  options.sort(cmpOption);
603
621
  let result = [], prev = null;
@@ -659,7 +677,8 @@ class CompletionState {
659
677
  let sources = conf.override ||
660
678
  state.languageDataAt("autocomplete", cur(state)).map(asSource);
661
679
  let active = sources.map(source => {
662
- let value = this.active.find(s => s.source == source) || new ActiveSource(source, 0 /* Inactive */, false);
680
+ let value = this.active.find(s => s.source == source) ||
681
+ new ActiveSource(source, this.active.some(a => a.state != 0 /* Inactive */) ? 1 /* Pending */ : 0 /* Inactive */);
663
682
  return value.update(tr, conf);
664
683
  });
665
684
  if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
@@ -668,7 +687,7 @@ class CompletionState {
668
687
  !sameResults(active, this.active) ? CompletionDialog.build(active, state, this.id, this.open)
669
688
  : this.open && tr.docChanged ? this.open.map(tr.changes) : this.open;
670
689
  if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
671
- active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */, false) : a);
690
+ active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
672
691
  for (let effect of tr.effects)
673
692
  if (effect.is(setSelectedEffect))
674
693
  open = open && open.setSelected(effect.value, this.id);
@@ -708,10 +727,10 @@ function cmpOption(a, b) {
708
727
  return lA < lB ? -1 : lA == lB ? 0 : 1;
709
728
  }
710
729
  class ActiveSource {
711
- constructor(source, state, explicit) {
730
+ constructor(source, state, explicitPos = -1) {
712
731
  this.source = source;
713
732
  this.state = state;
714
- this.explicit = explicit;
733
+ this.explicitPos = explicitPos;
715
734
  }
716
735
  hasResult() { return false; }
717
736
  update(tr, conf) {
@@ -721,12 +740,12 @@ class ActiveSource {
721
740
  else if (tr.docChanged)
722
741
  value = value.handleChange(tr);
723
742
  else if (tr.selection && value.state != 0 /* Inactive */)
724
- value = new ActiveSource(value.source, 0 /* Inactive */, false);
743
+ value = new ActiveSource(value.source, 0 /* Inactive */);
725
744
  for (let effect of tr.effects) {
726
745
  if (effect.is(startCompletionEffect))
727
- value = new ActiveSource(value.source, 1 /* Pending */, effect.value);
746
+ value = new ActiveSource(value.source, 1 /* Pending */, effect.value ? cur(tr.state) : -1);
728
747
  else if (effect.is(closeCompletionEffect))
729
- value = new ActiveSource(value.source, 0 /* Inactive */, false);
748
+ value = new ActiveSource(value.source, 0 /* Inactive */);
730
749
  else if (effect.is(setActiveEffect))
731
750
  for (let active of effect.value)
732
751
  if (active.source == value.source)
@@ -734,16 +753,19 @@ class ActiveSource {
734
753
  }
735
754
  return value;
736
755
  }
737
- handleUserEvent(_tr, type, conf) {
738
- return type == "delete" || !conf.activateOnTyping ? this : new ActiveSource(this.source, 1 /* Pending */, false);
756
+ handleUserEvent(tr, type, conf) {
757
+ return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* Pending */);
739
758
  }
740
759
  handleChange(tr) {
741
- return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */, false) : this;
760
+ return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
761
+ }
762
+ map(changes) {
763
+ return changes.empty || this.explicitPos < 0 ? this : new ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos));
742
764
  }
743
765
  }
744
766
  class ActiveResult extends ActiveSource {
745
- constructor(source, explicit, result, from, to, span) {
746
- super(source, 2 /* Result */, explicit);
767
+ constructor(source, explicitPos, result, from, to, span) {
768
+ super(source, 2 /* Result */, explicitPos);
747
769
  this.result = result;
748
770
  this.from = from;
749
771
  this.to = to;
@@ -753,25 +775,25 @@ class ActiveResult extends ActiveSource {
753
775
  handleUserEvent(tr, type, conf) {
754
776
  let from = tr.changes.mapPos(this.from), to = tr.changes.mapPos(this.to, 1);
755
777
  let pos = cur(tr.state);
756
- if ((this.explicit ? pos < from : pos <= from) || pos > to)
757
- return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */, false);
778
+ if ((this.explicitPos > -1 ? pos < from : pos <= from) || pos > to)
779
+ return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */);
780
+ let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos);
758
781
  if (this.span && (from == to || this.span.test(tr.state.sliceDoc(from, to))))
759
- return new ActiveResult(this.source, this.explicit, this.result, from, to, this.span);
760
- return new ActiveSource(this.source, 1 /* Pending */, this.explicit);
782
+ return new ActiveResult(this.source, explicitPos, this.result, from, to, this.span);
783
+ return new ActiveSource(this.source, 1 /* Pending */, explicitPos);
761
784
  }
762
785
  handleChange(tr) {
763
- return tr.changes.touchesRange(this.from, this.to)
764
- ? new ActiveSource(this.source, 0 /* Inactive */, false)
765
- : new ActiveResult(this.source, this.explicit, this.result, tr.changes.mapPos(this.from), tr.changes.mapPos(this.to, 1), this.span);
786
+ return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
766
787
  }
767
788
  map(mapping) {
768
- return new ActiveResult(this.source, this.explicit, this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1), this.span);
789
+ return mapping.empty ? this :
790
+ 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);
769
791
  }
770
792
  }
771
793
  const startCompletionEffect = state.StateEffect.define();
772
794
  const closeCompletionEffect = state.StateEffect.define();
773
795
  const setActiveEffect = state.StateEffect.define({
774
- map(sources, mapping) { return sources.map(s => s.hasResult() && !mapping.empty ? s.map(mapping) : s); }
796
+ map(sources, mapping) { return sources.map(s => s.map(mapping)); }
775
797
  });
776
798
  const setSelectedEffect = state.StateEffect.define();
777
799
  const completionState = state.StateField.define({
@@ -836,8 +858,8 @@ const closeCompletion = (view) => {
836
858
  return true;
837
859
  };
838
860
  class RunningQuery {
839
- constructor(source, context) {
840
- this.source = source;
861
+ constructor(active, context) {
862
+ this.active = active;
841
863
  this.context = context;
842
864
  this.time = Date.now();
843
865
  this.updates = [];
@@ -887,7 +909,7 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
887
909
  }
888
910
  if (this.debounceUpdate > -1)
889
911
  clearTimeout(this.debounceUpdate);
890
- this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.source == a.source))
912
+ this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.active.source == a.source))
891
913
  ? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
892
914
  if (this.composing != 0 /* None */)
893
915
  for (let tr of update.transactions) {
@@ -901,14 +923,14 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
901
923
  this.debounceUpdate = -1;
902
924
  let { state } = this.view, cState = state.field(completionState);
903
925
  for (let active of cState.active) {
904
- if (active.state == 1 /* Pending */ && !this.running.some(r => r.source == active.source))
926
+ if (active.state == 1 /* Pending */ && !this.running.some(r => r.active.source == active.source))
905
927
  this.startQuery(active);
906
928
  }
907
929
  }
908
930
  startQuery(active) {
909
931
  let { state } = this.view, pos = cur(state);
910
- let context = new CompletionContext(state, pos, active.explicit);
911
- let pending = new RunningQuery(active.source, context);
932
+ let context = new CompletionContext(state, pos, active.explicitPos == pos);
933
+ let pending = new RunningQuery(active, context);
912
934
  this.running.push(pending);
913
935
  Promise.resolve(active.source(context)).then(result => {
914
936
  if (!pending.context.aborted) {
@@ -941,7 +963,7 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
941
963
  continue;
942
964
  this.running.splice(i--, 1);
943
965
  if (query.done) {
944
- let active = new ActiveResult(query.source, query.context.explicit, 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 ? ensureAnchor(query.done.span, true) : null);
966
+ 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);
945
967
  // Replay the transactions that happened since the start of
946
968
  // the request and see if that preserves the result
947
969
  for (let tr of query.updates)
@@ -951,12 +973,12 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
951
973
  continue;
952
974
  }
953
975
  }
954
- let current = this.view.state.field(completionState).active.find(a => a.source == query.source);
976
+ let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
955
977
  if (current && current.state == 1 /* Pending */) {
956
978
  if (query.done == null) {
957
979
  // Explicitly failed. Should clear the pending status if it
958
980
  // hasn't been re-set in the meantime.
959
- let active = new ActiveSource(query.source, 0 /* Inactive */, false);
981
+ let active = new ActiveSource(query.active.source, 0 /* Inactive */);
960
982
  for (let tr of query.updates)
961
983
  active = active.update(tr, conf);
962
984
  if (active.state != 1 /* Pending */)
@@ -1034,7 +1056,7 @@ class Snippet {
1034
1056
  while (m = /[#$]\{(?:(\d+)(?::([^}]*))?|([^}]*))\}/.exec(line)) {
1035
1057
  let seq = m[1] ? +m[1] : null, name = m[2] || m[3], found = -1;
1036
1058
  for (let i = 0; i < fields.length; i++) {
1037
- if (name ? fields[i].name == name : seq != null && fields[i].seq == seq)
1059
+ if (seq != null ? fields[i].seq == seq : name ? fields[i].name == name : false)
1038
1060
  found = i;
1039
1061
  }
1040
1062
  if (found < 0) {
@@ -1043,6 +1065,9 @@ class Snippet {
1043
1065
  i++;
1044
1066
  fields.splice(i, 0, { seq, name: name || null });
1045
1067
  found = i;
1068
+ for (let pos of positions)
1069
+ if (pos.field >= found)
1070
+ pos.field++;
1046
1071
  }
1047
1072
  positions.push(new FieldPos(found, lines.length, m.index, m.index + name.length));
1048
1073
  line = line.slice(0, m.index) + name + line.slice(m.index + m[0].length);
@@ -1131,9 +1156,11 @@ function snippet(template) {
1131
1156
  if (ranges.length)
1132
1157
  spec.selection = fieldSelection(ranges, 0);
1133
1158
  if (ranges.length > 1) {
1134
- let effects = spec.effects = [setActive.of(new ActiveSnippet(ranges, 0))];
1159
+ let active = new ActiveSnippet(ranges, 0);
1160
+ let effects = spec.effects = [setActive.of(active)];
1135
1161
  if (editor.state.field(snippetState, false) === undefined)
1136
- effects.push(state.StateEffect.appendConfig.of([snippetState, addSnippetKeymap, snippetPointerHandler, baseTheme]));
1162
+ effects.push(state.StateEffect.appendConfig.of([snippetState.init(() => active), addSnippetKeymap,
1163
+ snippetPointerHandler, baseTheme]));
1137
1164
  }
1138
1165
  editor.dispatch(editor.state.update(spec));
1139
1166
  };
@@ -1309,6 +1336,7 @@ exports.completeFromList = completeFromList;
1309
1336
  exports.completionKeymap = completionKeymap;
1310
1337
  exports.completionStatus = completionStatus;
1311
1338
  exports.currentCompletions = currentCompletions;
1339
+ exports.ifIn = ifIn;
1312
1340
  exports.ifNotIn = ifNotIn;
1313
1341
  exports.moveCompletionSelection = moveCompletionSelection;
1314
1342
  exports.nextSnippetField = nextSnippetField;
package/dist/index.d.ts CHANGED
@@ -1,6 +1,5 @@
1
1
  import { EditorState, Transaction, StateCommand, Facet, Extension } from '@codemirror/state';
2
2
  import { EditorView, KeyBinding, Command } from '@codemirror/view';
3
- import { NodeType } from 'lezer-tree';
4
3
 
5
4
  interface CompletionConfig {
6
5
  /**
@@ -66,6 +65,8 @@ interface Completion {
66
65
  library defines simple icons for `class`, `constant`, `enum`,
67
66
  `function`, `interface`, `keyword`, `method`, `namespace`,
68
67
  `property`, `text`, `type`, and `variable`.
68
+
69
+ Multiple types can be provided by separating them with spaces.
69
70
  */
70
71
  type?: string;
71
72
  /**
@@ -121,10 +122,10 @@ declare class CompletionContext {
121
122
  token before `this.pos`.
122
123
  */
123
124
  tokenBefore(types: readonly string[]): {
124
- from: number;
125
+ from: any;
125
126
  to: number;
126
127
  text: string;
127
- type: NodeType;
128
+ type: any;
128
129
  } | null;
129
130
  /**
130
131
  Get the match of the given expression directly before the
@@ -153,6 +154,11 @@ completes them.
153
154
  */
154
155
  declare function completeFromList(list: readonly (string | Completion)[]): CompletionSource;
155
156
  /**
157
+ Wrap the given completion source so that it will only fire when the
158
+ cursor is in a syntax node with one of the given names.
159
+ */
160
+ declare function ifIn(nodes: readonly string[], source: CompletionSource): CompletionSource;
161
+ /**
156
162
  Wrap the given completion source so that it will not fire when the
157
163
  cursor is in a syntax node with one of the given names.
158
164
  */
@@ -193,6 +199,15 @@ interface CompletionResult {
193
199
  list to be updated synchronously.
194
200
  */
195
201
  span?: RegExp;
202
+ /**
203
+ By default, the library filters and scores completions. Set
204
+ `filter` to `false` to disable this, and cause your completions
205
+ to all be included, in the order they were given. When there are
206
+ other sources, unfiltered completions appear at the top of the
207
+ list of completions. `span` must not be given `filter` is
208
+ `false`, because it only works when filtering.
209
+ */
210
+ filter?: boolean;
196
211
  }
197
212
 
198
213
  /**
@@ -304,4 +319,4 @@ Returns the available completions as an array.
304
319
  */
305
320
  declare function currentCompletions(state: EditorState): readonly Completion[];
306
321
 
307
- export { Completion, CompletionContext, CompletionResult, CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifNotIn, moveCompletionSelection, nextSnippetField, prevSnippetField, snippet, snippetCompletion, snippetKeymap, startCompletion };
322
+ export { Completion, CompletionContext, CompletionResult, CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifIn, ifNotIn, moveCompletionSelection, nextSnippetField, prevSnippetField, snippet, snippetCompletion, snippetKeymap, startCompletion };
package/dist/index.js CHANGED
@@ -105,6 +105,18 @@ function completeFromList(list) {
105
105
  };
106
106
  }
107
107
  /**
108
+ Wrap the given completion source so that it will only fire when the
109
+ cursor is in a syntax node with one of the given names.
110
+ */
111
+ function ifIn(nodes, source) {
112
+ return (context) => {
113
+ for (let pos = syntaxTree(context.state).resolve(context.pos, -1); pos; pos = pos.parent)
114
+ if (nodes.indexOf(pos.name) > -1)
115
+ return source(context);
116
+ return null;
117
+ };
118
+ }
119
+ /**
108
120
  Wrap the given completion source so that it will not fire when the
109
121
  cursor is in a syntax node with one of the given names.
110
122
  */
@@ -402,7 +414,7 @@ function createListBox(options, id, range) {
402
414
  let icon = li.appendChild(document.createElement("div"));
403
415
  icon.classList.add("cm-completionIcon");
404
416
  if (completion.type)
405
- icon.classList.add("cm-completionIcon-" + completion.type);
417
+ icon.classList.add(...completion.type.split(/\s+/g).map(cls => "cm-completionIcon-" + cls));
406
418
  icon.setAttribute("aria-hidden", "true");
407
419
  let labelElt = li.appendChild(document.createElement("span"));
408
420
  labelElt.className = "cm-completionLabel";
@@ -584,16 +596,22 @@ function score(option) {
584
596
  (option.type ? 1 : 0);
585
597
  }
586
598
  function sortOptions(active, state) {
587
- let options = [];
599
+ let options = [], i = 0;
588
600
  for (let a of active)
589
601
  if (a.hasResult()) {
590
- let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match;
591
- for (let option of a.result.options)
592
- if (match = matcher.match(option.label)) {
593
- if (option.boost != null)
594
- match[0] += option.boost;
595
- options.push(new Option(option, a, match));
596
- }
602
+ if (a.result.filter === false) {
603
+ for (let option of a.result.options)
604
+ options.push(new Option(option, a, [1e9 - i++]));
605
+ }
606
+ else {
607
+ let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match;
608
+ for (let option of a.result.options)
609
+ if (match = matcher.match(option.label)) {
610
+ if (option.boost != null)
611
+ match[0] += option.boost;
612
+ options.push(new Option(option, a, match));
613
+ }
614
+ }
597
615
  }
598
616
  options.sort(cmpOption);
599
617
  let result = [], prev = null;
@@ -655,7 +673,8 @@ class CompletionState {
655
673
  let sources = conf.override ||
656
674
  state.languageDataAt("autocomplete", cur(state)).map(asSource);
657
675
  let active = sources.map(source => {
658
- let value = this.active.find(s => s.source == source) || new ActiveSource(source, 0 /* Inactive */, false);
676
+ let value = this.active.find(s => s.source == source) ||
677
+ new ActiveSource(source, this.active.some(a => a.state != 0 /* Inactive */) ? 1 /* Pending */ : 0 /* Inactive */);
659
678
  return value.update(tr, conf);
660
679
  });
661
680
  if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
@@ -664,7 +683,7 @@ class CompletionState {
664
683
  !sameResults(active, this.active) ? CompletionDialog.build(active, state, this.id, this.open)
665
684
  : this.open && tr.docChanged ? this.open.map(tr.changes) : this.open;
666
685
  if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
667
- active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */, false) : a);
686
+ active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
668
687
  for (let effect of tr.effects)
669
688
  if (effect.is(setSelectedEffect))
670
689
  open = open && open.setSelected(effect.value, this.id);
@@ -704,10 +723,10 @@ function cmpOption(a, b) {
704
723
  return lA < lB ? -1 : lA == lB ? 0 : 1;
705
724
  }
706
725
  class ActiveSource {
707
- constructor(source, state, explicit) {
726
+ constructor(source, state, explicitPos = -1) {
708
727
  this.source = source;
709
728
  this.state = state;
710
- this.explicit = explicit;
729
+ this.explicitPos = explicitPos;
711
730
  }
712
731
  hasResult() { return false; }
713
732
  update(tr, conf) {
@@ -717,12 +736,12 @@ class ActiveSource {
717
736
  else if (tr.docChanged)
718
737
  value = value.handleChange(tr);
719
738
  else if (tr.selection && value.state != 0 /* Inactive */)
720
- value = new ActiveSource(value.source, 0 /* Inactive */, false);
739
+ value = new ActiveSource(value.source, 0 /* Inactive */);
721
740
  for (let effect of tr.effects) {
722
741
  if (effect.is(startCompletionEffect))
723
- value = new ActiveSource(value.source, 1 /* Pending */, effect.value);
742
+ value = new ActiveSource(value.source, 1 /* Pending */, effect.value ? cur(tr.state) : -1);
724
743
  else if (effect.is(closeCompletionEffect))
725
- value = new ActiveSource(value.source, 0 /* Inactive */, false);
744
+ value = new ActiveSource(value.source, 0 /* Inactive */);
726
745
  else if (effect.is(setActiveEffect))
727
746
  for (let active of effect.value)
728
747
  if (active.source == value.source)
@@ -730,16 +749,19 @@ class ActiveSource {
730
749
  }
731
750
  return value;
732
751
  }
733
- handleUserEvent(_tr, type, conf) {
734
- return type == "delete" || !conf.activateOnTyping ? this : new ActiveSource(this.source, 1 /* Pending */, false);
752
+ handleUserEvent(tr, type, conf) {
753
+ return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* Pending */);
735
754
  }
736
755
  handleChange(tr) {
737
- return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */, false) : this;
756
+ return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
757
+ }
758
+ map(changes) {
759
+ return changes.empty || this.explicitPos < 0 ? this : new ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos));
738
760
  }
739
761
  }
740
762
  class ActiveResult extends ActiveSource {
741
- constructor(source, explicit, result, from, to, span) {
742
- super(source, 2 /* Result */, explicit);
763
+ constructor(source, explicitPos, result, from, to, span) {
764
+ super(source, 2 /* Result */, explicitPos);
743
765
  this.result = result;
744
766
  this.from = from;
745
767
  this.to = to;
@@ -749,25 +771,25 @@ class ActiveResult extends ActiveSource {
749
771
  handleUserEvent(tr, type, conf) {
750
772
  let from = tr.changes.mapPos(this.from), to = tr.changes.mapPos(this.to, 1);
751
773
  let pos = cur(tr.state);
752
- if ((this.explicit ? pos < from : pos <= from) || pos > to)
753
- return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */, false);
774
+ if ((this.explicitPos > -1 ? pos < from : pos <= from) || pos > to)
775
+ return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */);
776
+ let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos);
754
777
  if (this.span && (from == to || this.span.test(tr.state.sliceDoc(from, to))))
755
- return new ActiveResult(this.source, this.explicit, this.result, from, to, this.span);
756
- return new ActiveSource(this.source, 1 /* Pending */, this.explicit);
778
+ return new ActiveResult(this.source, explicitPos, this.result, from, to, this.span);
779
+ return new ActiveSource(this.source, 1 /* Pending */, explicitPos);
757
780
  }
758
781
  handleChange(tr) {
759
- return tr.changes.touchesRange(this.from, this.to)
760
- ? new ActiveSource(this.source, 0 /* Inactive */, false)
761
- : new ActiveResult(this.source, this.explicit, this.result, tr.changes.mapPos(this.from), tr.changes.mapPos(this.to, 1), this.span);
782
+ return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
762
783
  }
763
784
  map(mapping) {
764
- return new ActiveResult(this.source, this.explicit, this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1), this.span);
785
+ return mapping.empty ? this :
786
+ 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);
765
787
  }
766
788
  }
767
789
  const startCompletionEffect = /*@__PURE__*/StateEffect.define();
768
790
  const closeCompletionEffect = /*@__PURE__*/StateEffect.define();
769
791
  const setActiveEffect = /*@__PURE__*/StateEffect.define({
770
- map(sources, mapping) { return sources.map(s => s.hasResult() && !mapping.empty ? s.map(mapping) : s); }
792
+ map(sources, mapping) { return sources.map(s => s.map(mapping)); }
771
793
  });
772
794
  const setSelectedEffect = /*@__PURE__*/StateEffect.define();
773
795
  const completionState = /*@__PURE__*/StateField.define({
@@ -832,8 +854,8 @@ const closeCompletion = (view) => {
832
854
  return true;
833
855
  };
834
856
  class RunningQuery {
835
- constructor(source, context) {
836
- this.source = source;
857
+ constructor(active, context) {
858
+ this.active = active;
837
859
  this.context = context;
838
860
  this.time = Date.now();
839
861
  this.updates = [];
@@ -883,7 +905,7 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
883
905
  }
884
906
  if (this.debounceUpdate > -1)
885
907
  clearTimeout(this.debounceUpdate);
886
- this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.source == a.source))
908
+ this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.active.source == a.source))
887
909
  ? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
888
910
  if (this.composing != 0 /* None */)
889
911
  for (let tr of update.transactions) {
@@ -897,14 +919,14 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
897
919
  this.debounceUpdate = -1;
898
920
  let { state } = this.view, cState = state.field(completionState);
899
921
  for (let active of cState.active) {
900
- if (active.state == 1 /* Pending */ && !this.running.some(r => r.source == active.source))
922
+ if (active.state == 1 /* Pending */ && !this.running.some(r => r.active.source == active.source))
901
923
  this.startQuery(active);
902
924
  }
903
925
  }
904
926
  startQuery(active) {
905
927
  let { state } = this.view, pos = cur(state);
906
- let context = new CompletionContext(state, pos, active.explicit);
907
- let pending = new RunningQuery(active.source, context);
928
+ let context = new CompletionContext(state, pos, active.explicitPos == pos);
929
+ let pending = new RunningQuery(active, context);
908
930
  this.running.push(pending);
909
931
  Promise.resolve(active.source(context)).then(result => {
910
932
  if (!pending.context.aborted) {
@@ -937,7 +959,7 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
937
959
  continue;
938
960
  this.running.splice(i--, 1);
939
961
  if (query.done) {
940
- let active = new ActiveResult(query.source, query.context.explicit, 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 ? ensureAnchor(query.done.span, true) : null);
962
+ 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);
941
963
  // Replay the transactions that happened since the start of
942
964
  // the request and see if that preserves the result
943
965
  for (let tr of query.updates)
@@ -947,12 +969,12 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
947
969
  continue;
948
970
  }
949
971
  }
950
- let current = this.view.state.field(completionState).active.find(a => a.source == query.source);
972
+ let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
951
973
  if (current && current.state == 1 /* Pending */) {
952
974
  if (query.done == null) {
953
975
  // Explicitly failed. Should clear the pending status if it
954
976
  // hasn't been re-set in the meantime.
955
- let active = new ActiveSource(query.source, 0 /* Inactive */, false);
977
+ let active = new ActiveSource(query.active.source, 0 /* Inactive */);
956
978
  for (let tr of query.updates)
957
979
  active = active.update(tr, conf);
958
980
  if (active.state != 1 /* Pending */)
@@ -1030,7 +1052,7 @@ class Snippet {
1030
1052
  while (m = /[#$]\{(?:(\d+)(?::([^}]*))?|([^}]*))\}/.exec(line)) {
1031
1053
  let seq = m[1] ? +m[1] : null, name = m[2] || m[3], found = -1;
1032
1054
  for (let i = 0; i < fields.length; i++) {
1033
- if (name ? fields[i].name == name : seq != null && fields[i].seq == seq)
1055
+ if (seq != null ? fields[i].seq == seq : name ? fields[i].name == name : false)
1034
1056
  found = i;
1035
1057
  }
1036
1058
  if (found < 0) {
@@ -1039,6 +1061,9 @@ class Snippet {
1039
1061
  i++;
1040
1062
  fields.splice(i, 0, { seq, name: name || null });
1041
1063
  found = i;
1064
+ for (let pos of positions)
1065
+ if (pos.field >= found)
1066
+ pos.field++;
1042
1067
  }
1043
1068
  positions.push(new FieldPos(found, lines.length, m.index, m.index + name.length));
1044
1069
  line = line.slice(0, m.index) + name + line.slice(m.index + m[0].length);
@@ -1127,9 +1152,11 @@ function snippet(template) {
1127
1152
  if (ranges.length)
1128
1153
  spec.selection = fieldSelection(ranges, 0);
1129
1154
  if (ranges.length > 1) {
1130
- let effects = spec.effects = [setActive.of(new ActiveSnippet(ranges, 0))];
1155
+ let active = new ActiveSnippet(ranges, 0);
1156
+ let effects = spec.effects = [setActive.of(active)];
1131
1157
  if (editor.state.field(snippetState, false) === undefined)
1132
- effects.push(StateEffect.appendConfig.of([snippetState, addSnippetKeymap, snippetPointerHandler, baseTheme]));
1158
+ effects.push(StateEffect.appendConfig.of([snippetState.init(() => active), addSnippetKeymap,
1159
+ snippetPointerHandler, baseTheme]));
1133
1160
  }
1134
1161
  editor.dispatch(editor.state.update(spec));
1135
1162
  };
@@ -1295,4 +1322,4 @@ function currentCompletions(state) {
1295
1322
  return open ? open.options.map(o => o.completion) : [];
1296
1323
  }
1297
1324
 
1298
- export { CompletionContext, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifNotIn, moveCompletionSelection, nextSnippetField, prevSnippetField, snippet, snippetCompletion, snippetKeymap, startCompletion };
1325
+ export { CompletionContext, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifIn, ifNotIn, moveCompletionSelection, nextSnippetField, prevSnippetField, snippet, snippetCompletion, snippetKeymap, startCompletion };
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@codemirror/autocomplete",
3
- "version": "0.18.4",
3
+ "version": "0.18.8",
4
4
  "description": "Autocompletion for the CodeMirror code editor",
5
5
  "scripts": {
6
- "test": "echo 'Test runner in wrapper package'",
6
+ "test": "cm-runtests",
7
7
  "prepare": "cm-buildhelper src/index.ts"
8
8
  },
9
9
  "keywords": [
@@ -34,10 +34,7 @@
34
34
  "lezer-tree": "^0.13.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@codemirror/buildhelper": "^0.1.0",
38
- "@types/mocha": "^5.2.0",
39
- "ist": "^1.1.6",
40
- "mocha": "^7.1.1"
37
+ "@codemirror/buildhelper": "^0.1.5"
41
38
  },
42
39
  "repository": {
43
40
  "type": "git",