@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 +32 -0
- package/dist/index.cjs +70 -42
- package/dist/index.d.ts +19 -4
- package/dist/index.js +70 -43
- package/package.json +3 -6
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-" +
|
|
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
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
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) ||
|
|
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
|
|
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,
|
|
730
|
+
constructor(source, state, explicitPos = -1) {
|
|
712
731
|
this.source = source;
|
|
713
732
|
this.state = state;
|
|
714
|
-
this.
|
|
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
|
|
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
|
|
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(
|
|
738
|
-
return type == "delete" || !conf.activateOnTyping ? this : new ActiveSource(this.source, 1 /* Pending
|
|
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
|
|
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,
|
|
746
|
-
super(source, 2 /* Result */,
|
|
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.
|
|
757
|
-
return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive
|
|
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,
|
|
760
|
-
return new ActiveSource(this.source, 1 /* Pending */,
|
|
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
|
|
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.
|
|
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(
|
|
840
|
-
this.
|
|
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.
|
|
911
|
-
let pending = new RunningQuery(active
|
|
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.
|
|
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
|
|
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 (
|
|
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
|
|
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,
|
|
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:
|
|
125
|
+
from: any;
|
|
125
126
|
to: number;
|
|
126
127
|
text: string;
|
|
127
|
-
type:
|
|
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-" +
|
|
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
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
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) ||
|
|
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
|
|
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,
|
|
726
|
+
constructor(source, state, explicitPos = -1) {
|
|
708
727
|
this.source = source;
|
|
709
728
|
this.state = state;
|
|
710
|
-
this.
|
|
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
|
|
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
|
|
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(
|
|
734
|
-
return type == "delete" || !conf.activateOnTyping ? this : new ActiveSource(this.source, 1 /* Pending
|
|
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
|
|
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,
|
|
742
|
-
super(source, 2 /* Result */,
|
|
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.
|
|
753
|
-
return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive
|
|
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,
|
|
756
|
-
return new ActiveSource(this.source, 1 /* Pending */,
|
|
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
|
|
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.
|
|
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(
|
|
836
|
-
this.
|
|
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.
|
|
907
|
-
let pending = new RunningQuery(active
|
|
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.
|
|
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
|
|
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 (
|
|
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
|
|
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,
|
|
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.
|
|
3
|
+
"version": "0.18.8",
|
|
4
4
|
"description": "Autocompletion for the CodeMirror code editor",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"test": "
|
|
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.
|
|
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",
|