@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/CHANGELOG.md +36 -0
- package/dist/index.cjs +355 -64
- package/dist/index.d.ts +98 -16
- package/dist/index.js +333 -48
- package/package.json +5 -7
package/dist/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import { Annotation, Facet, combineConfig, StateEffect, StateField, Prec,
|
|
2
|
-
import { Direction,
|
|
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 [
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
160
|
-
|
|
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
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
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
|
-
|
|
562
|
-
|
|
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
|
|
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
|
|
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 (
|
|
746
|
-
return new ActiveResult(this.source, explicitPos, this.result, from, to
|
|
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)
|
|
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)
|
|
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
|
|
1204
|
-
Snippets are written
|
|
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,
|
|
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
|
-
|
|
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.
|
|
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.
|
|
30
|
-
"@codemirror/state": "^0.
|
|
31
|
-
"@codemirror/
|
|
32
|
-
"@
|
|
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"
|