@codemirror/autocomplete 6.1.1 → 6.3.0
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 +12 -0
- package/dist/index.cjs +34 -16
- package/dist/index.d.ts +14 -1
- package/dist/index.js +34 -16
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 6.3.0 (2022-09-22)
|
|
2
|
+
|
|
3
|
+
### New features
|
|
4
|
+
|
|
5
|
+
Close bracket configuration now supports a `stringPrefixes` property that can be used to allow autoclosing of prefixed strings.
|
|
6
|
+
|
|
7
|
+
## 6.2.0 (2022-09-13)
|
|
8
|
+
|
|
9
|
+
### New features
|
|
10
|
+
|
|
11
|
+
Autocompletion now takes an `interactionDelay` option that can be used to control the delay between the time where completion opens and the time where commands like `acceptCompletion` affect it.
|
|
12
|
+
|
|
1
13
|
## 6.1.1 (2022-09-08)
|
|
2
14
|
|
|
3
15
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -331,7 +331,8 @@ const completionConfig = state.Facet.define({
|
|
|
331
331
|
aboveCursor: false,
|
|
332
332
|
icons: true,
|
|
333
333
|
addToOptions: [],
|
|
334
|
-
compareCompletions: (a, b) => a.label.localeCompare(b.label)
|
|
334
|
+
compareCompletions: (a, b) => a.label.localeCompare(b.label),
|
|
335
|
+
interactionDelay: 75
|
|
335
336
|
}, {
|
|
336
337
|
defaultKeymap: (a, b) => a && b,
|
|
337
338
|
closeOnBlur: (a, b) => a && b,
|
|
@@ -508,7 +509,7 @@ class CompletionTooltip {
|
|
|
508
509
|
let sel = this.dom.querySelector("[aria-selected]");
|
|
509
510
|
if (!sel || !this.info)
|
|
510
511
|
return null;
|
|
511
|
-
let win = this.dom.ownerDocument.defaultView;
|
|
512
|
+
let win = this.dom.ownerDocument.defaultView || window;
|
|
512
513
|
let listRect = this.dom.getBoundingClientRect();
|
|
513
514
|
let infoRect = this.info.getBoundingClientRect();
|
|
514
515
|
let selRect = sel.getBoundingClientRect();
|
|
@@ -826,7 +827,6 @@ const completionState = state.StateField.define({
|
|
|
826
827
|
]
|
|
827
828
|
});
|
|
828
829
|
|
|
829
|
-
const CompletionInteractMargin = 75;
|
|
830
830
|
/**
|
|
831
831
|
Returns a command that moves the completion selection forward or
|
|
832
832
|
backward by the given amount.
|
|
@@ -834,7 +834,8 @@ backward by the given amount.
|
|
|
834
834
|
function moveCompletionSelection(forward, by = "option") {
|
|
835
835
|
return (view$1) => {
|
|
836
836
|
let cState = view$1.state.field(completionState, false);
|
|
837
|
-
if (!cState || !cState.open ||
|
|
837
|
+
if (!cState || !cState.open ||
|
|
838
|
+
Date.now() - cState.open.timestamp < view$1.state.facet(completionConfig).interactionDelay)
|
|
838
839
|
return false;
|
|
839
840
|
let step = 1, tooltip;
|
|
840
841
|
if (by == "page" && (tooltip = view.getTooltip(view$1, cState.open.tooltip)))
|
|
@@ -855,8 +856,8 @@ Accept the current completion.
|
|
|
855
856
|
*/
|
|
856
857
|
const acceptCompletion = (view) => {
|
|
857
858
|
let cState = view.state.field(completionState, false);
|
|
858
|
-
if (view.state.readOnly || !cState || !cState.open ||
|
|
859
|
-
cState.open.
|
|
859
|
+
if (view.state.readOnly || !cState || !cState.open || cState.open.selected < 0 ||
|
|
860
|
+
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
|
860
861
|
return false;
|
|
861
862
|
applyCompletion(view, cState.open.options[cState.open.selected]);
|
|
862
863
|
return true;
|
|
@@ -1090,6 +1091,7 @@ const baseTheme = view.EditorView.baseTheme({
|
|
|
1090
1091
|
verticalAlign: "text-top",
|
|
1091
1092
|
width: 0,
|
|
1092
1093
|
height: "1.15em",
|
|
1094
|
+
display: "inline-block",
|
|
1093
1095
|
margin: "0 -0.7px -.7em",
|
|
1094
1096
|
borderLeft: "1.4px dotted #888"
|
|
1095
1097
|
},
|
|
@@ -1473,7 +1475,8 @@ const completeAnyWord = context => {
|
|
|
1473
1475
|
|
|
1474
1476
|
const defaults = {
|
|
1475
1477
|
brackets: ["(", "[", "{", "'", '"'],
|
|
1476
|
-
before: ")]}:;>"
|
|
1478
|
+
before: ")]}:;>",
|
|
1479
|
+
stringPrefixes: []
|
|
1477
1480
|
};
|
|
1478
1481
|
const closeBracketEffect = state.StateEffect.define({
|
|
1479
1482
|
map(value, mapping) {
|
|
@@ -1589,7 +1592,7 @@ function insertBracket(state$1, bracket) {
|
|
|
1589
1592
|
for (let tok of tokens) {
|
|
1590
1593
|
let closed = closing(state.codePointAt(tok, 0));
|
|
1591
1594
|
if (bracket == tok)
|
|
1592
|
-
return closed == tok ? handleSame(state$1, tok, tokens.indexOf(tok + tok + tok) > -1)
|
|
1595
|
+
return closed == tok ? handleSame(state$1, tok, tokens.indexOf(tok + tok + tok) > -1, conf)
|
|
1593
1596
|
: handleOpen(state$1, tok, closed, conf.before || defaults.before);
|
|
1594
1597
|
if (bracket == closed && closedBracketAt(state$1, state$1.selection.main.from))
|
|
1595
1598
|
return handleClose(state$1, tok, closed);
|
|
@@ -1644,13 +1647,14 @@ function handleClose(state$1, _open, close) {
|
|
|
1644
1647
|
}
|
|
1645
1648
|
// Handles cases where the open and close token are the same, and
|
|
1646
1649
|
// possibly triple quotes (as in `"""abc"""`-style quoting).
|
|
1647
|
-
function handleSame(state$1, token, allowTriple) {
|
|
1650
|
+
function handleSame(state$1, token, allowTriple, config) {
|
|
1651
|
+
let stringPrefixes = config.stringPrefixes || defaults.stringPrefixes;
|
|
1648
1652
|
let dont = null, changes = state$1.changeByRange(range => {
|
|
1649
1653
|
if (!range.empty)
|
|
1650
1654
|
return { changes: [{ insert: token, from: range.from }, { insert: token, from: range.to }],
|
|
1651
1655
|
effects: closeBracketEffect.of(range.to + token.length),
|
|
1652
1656
|
range: state.EditorSelection.range(range.anchor + token.length, range.head + token.length) };
|
|
1653
|
-
let pos = range.head, next = nextChar(state$1.doc, pos);
|
|
1657
|
+
let pos = range.head, next = nextChar(state$1.doc, pos), start;
|
|
1654
1658
|
if (next == token) {
|
|
1655
1659
|
if (nodeStart(state$1, pos)) {
|
|
1656
1660
|
return { changes: { insert: token + token, from: pos },
|
|
@@ -1664,14 +1668,14 @@ function handleSame(state$1, token, allowTriple) {
|
|
|
1664
1668
|
}
|
|
1665
1669
|
}
|
|
1666
1670
|
else if (allowTriple && state$1.sliceDoc(pos - 2 * token.length, pos) == token + token &&
|
|
1667
|
-
|
|
1671
|
+
(start = canStartStringAt(state$1, pos - 2 * token.length, stringPrefixes)) > -1 &&
|
|
1672
|
+
nodeStart(state$1, start)) {
|
|
1668
1673
|
return { changes: { insert: token + token + token + token, from: pos },
|
|
1669
1674
|
effects: closeBracketEffect.of(pos + token.length),
|
|
1670
1675
|
range: state.EditorSelection.cursor(pos + token.length) };
|
|
1671
1676
|
}
|
|
1672
1677
|
else if (state$1.charCategorizer(pos)(next) != state.CharCategory.Word) {
|
|
1673
|
-
|
|
1674
|
-
if (prev != token && state$1.charCategorizer(pos)(prev) != state.CharCategory.Word && !probablyInString(state$1, pos, token))
|
|
1678
|
+
if (canStartStringAt(state$1, pos, stringPrefixes) > -1 && !probablyInString(state$1, pos, token, stringPrefixes))
|
|
1675
1679
|
return { changes: { insert: token + token, from: pos },
|
|
1676
1680
|
effects: closeBracketEffect.of(pos + token.length),
|
|
1677
1681
|
range: state.EditorSelection.cursor(pos + token.length) };
|
|
@@ -1687,12 +1691,15 @@ function nodeStart(state, pos) {
|
|
|
1687
1691
|
let tree = language.syntaxTree(state).resolveInner(pos + 1);
|
|
1688
1692
|
return tree.parent && tree.from == pos;
|
|
1689
1693
|
}
|
|
1690
|
-
function probablyInString(state, pos, quoteToken) {
|
|
1694
|
+
function probablyInString(state, pos, quoteToken, prefixes) {
|
|
1691
1695
|
let node = language.syntaxTree(state).resolveInner(pos, -1);
|
|
1696
|
+
let maxPrefix = prefixes.reduce((m, p) => Math.max(m, p.length), 0);
|
|
1692
1697
|
for (let i = 0; i < 5; i++) {
|
|
1693
|
-
|
|
1698
|
+
let start = state.sliceDoc(node.from, Math.min(node.to, node.from + quoteToken.length + maxPrefix));
|
|
1699
|
+
let quotePos = start.indexOf(quoteToken);
|
|
1700
|
+
if (!quotePos || quotePos > -1 && prefixes.indexOf(start.slice(0, quotePos)) > -1) {
|
|
1694
1701
|
let first = node.firstChild;
|
|
1695
|
-
while (first && first.from == node.from && first.to - first.from > quoteToken.length) {
|
|
1702
|
+
while (first && first.from == node.from && first.to - first.from > quoteToken.length + quotePos) {
|
|
1696
1703
|
if (state.sliceDoc(first.to - quoteToken.length, first.to) == quoteToken)
|
|
1697
1704
|
return false;
|
|
1698
1705
|
first = first.firstChild;
|
|
@@ -1706,6 +1713,17 @@ function probablyInString(state, pos, quoteToken) {
|
|
|
1706
1713
|
}
|
|
1707
1714
|
return false;
|
|
1708
1715
|
}
|
|
1716
|
+
function canStartStringAt(state$1, pos, prefixes) {
|
|
1717
|
+
let charCat = state$1.charCategorizer(pos);
|
|
1718
|
+
if (charCat(state$1.sliceDoc(pos - 1, pos)) != state.CharCategory.Word)
|
|
1719
|
+
return pos;
|
|
1720
|
+
for (let prefix of prefixes) {
|
|
1721
|
+
let start = pos - prefix.length;
|
|
1722
|
+
if (state$1.sliceDoc(start, pos) == prefix && charCat(state$1.sliceDoc(start - 1, start)) != state.CharCategory.Word)
|
|
1723
|
+
return start;
|
|
1724
|
+
}
|
|
1725
|
+
return -1;
|
|
1726
|
+
}
|
|
1709
1727
|
|
|
1710
1728
|
/**
|
|
1711
1729
|
Returns an extension that enables autocompletion.
|
package/dist/index.d.ts
CHANGED
|
@@ -66,7 +66,8 @@ interface CompletionConfig {
|
|
|
66
66
|
completion, and should produce a DOM node to show. `position`
|
|
67
67
|
determines where in the DOM the result appears, relative to
|
|
68
68
|
other added widgets and the standard content. The default icons
|
|
69
|
-
have position 20, the label position 50, and the detail position
|
|
69
|
+
have position 20, the label position 50, and the detail position
|
|
70
|
+
80.
|
|
70
71
|
*/
|
|
71
72
|
addToOptions?: {
|
|
72
73
|
render: (completion: Completion, state: EditorState) => Node | null;
|
|
@@ -78,6 +79,13 @@ interface CompletionConfig {
|
|
|
78
79
|
[`localeCompare`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
|
|
79
80
|
*/
|
|
80
81
|
compareCompletions?: (a: Completion, b: Completion) => number;
|
|
82
|
+
/**
|
|
83
|
+
By default, commands relating to an open completion only take
|
|
84
|
+
effect 75 milliseconds after the completion opened, so that key
|
|
85
|
+
presses made before the user is aware of the tooltip don't go to
|
|
86
|
+
the tooltip. This option can be used to configure that delay.
|
|
87
|
+
*/
|
|
88
|
+
interactionDelay?: number;
|
|
81
89
|
}
|
|
82
90
|
|
|
83
91
|
/**
|
|
@@ -397,6 +405,11 @@ interface CloseBracketConfig {
|
|
|
397
405
|
whitespace. Defaults to `")]}:;>"`.
|
|
398
406
|
*/
|
|
399
407
|
before?: string;
|
|
408
|
+
/**
|
|
409
|
+
When determining whether a given node may be a string, recognize
|
|
410
|
+
these prefixes before the opening quote.
|
|
411
|
+
*/
|
|
412
|
+
stringPrefixes?: string[];
|
|
400
413
|
}
|
|
401
414
|
/**
|
|
402
415
|
Extension to enable bracket-closing behavior. When a closeable
|
package/dist/index.js
CHANGED
|
@@ -327,7 +327,8 @@ const completionConfig = /*@__PURE__*/Facet.define({
|
|
|
327
327
|
aboveCursor: false,
|
|
328
328
|
icons: true,
|
|
329
329
|
addToOptions: [],
|
|
330
|
-
compareCompletions: (a, b) => a.label.localeCompare(b.label)
|
|
330
|
+
compareCompletions: (a, b) => a.label.localeCompare(b.label),
|
|
331
|
+
interactionDelay: 75
|
|
331
332
|
}, {
|
|
332
333
|
defaultKeymap: (a, b) => a && b,
|
|
333
334
|
closeOnBlur: (a, b) => a && b,
|
|
@@ -504,7 +505,7 @@ class CompletionTooltip {
|
|
|
504
505
|
let sel = this.dom.querySelector("[aria-selected]");
|
|
505
506
|
if (!sel || !this.info)
|
|
506
507
|
return null;
|
|
507
|
-
let win = this.dom.ownerDocument.defaultView;
|
|
508
|
+
let win = this.dom.ownerDocument.defaultView || window;
|
|
508
509
|
let listRect = this.dom.getBoundingClientRect();
|
|
509
510
|
let infoRect = this.info.getBoundingClientRect();
|
|
510
511
|
let selRect = sel.getBoundingClientRect();
|
|
@@ -822,7 +823,6 @@ const completionState = /*@__PURE__*/StateField.define({
|
|
|
822
823
|
]
|
|
823
824
|
});
|
|
824
825
|
|
|
825
|
-
const CompletionInteractMargin = 75;
|
|
826
826
|
/**
|
|
827
827
|
Returns a command that moves the completion selection forward or
|
|
828
828
|
backward by the given amount.
|
|
@@ -830,7 +830,8 @@ backward by the given amount.
|
|
|
830
830
|
function moveCompletionSelection(forward, by = "option") {
|
|
831
831
|
return (view) => {
|
|
832
832
|
let cState = view.state.field(completionState, false);
|
|
833
|
-
if (!cState || !cState.open ||
|
|
833
|
+
if (!cState || !cState.open ||
|
|
834
|
+
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
|
834
835
|
return false;
|
|
835
836
|
let step = 1, tooltip;
|
|
836
837
|
if (by == "page" && (tooltip = getTooltip(view, cState.open.tooltip)))
|
|
@@ -851,8 +852,8 @@ Accept the current completion.
|
|
|
851
852
|
*/
|
|
852
853
|
const acceptCompletion = (view) => {
|
|
853
854
|
let cState = view.state.field(completionState, false);
|
|
854
|
-
if (view.state.readOnly || !cState || !cState.open ||
|
|
855
|
-
cState.open.
|
|
855
|
+
if (view.state.readOnly || !cState || !cState.open || cState.open.selected < 0 ||
|
|
856
|
+
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
|
856
857
|
return false;
|
|
857
858
|
applyCompletion(view, cState.open.options[cState.open.selected]);
|
|
858
859
|
return true;
|
|
@@ -1086,6 +1087,7 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
|
|
|
1086
1087
|
verticalAlign: "text-top",
|
|
1087
1088
|
width: 0,
|
|
1088
1089
|
height: "1.15em",
|
|
1090
|
+
display: "inline-block",
|
|
1089
1091
|
margin: "0 -0.7px -.7em",
|
|
1090
1092
|
borderLeft: "1.4px dotted #888"
|
|
1091
1093
|
},
|
|
@@ -1469,7 +1471,8 @@ const completeAnyWord = context => {
|
|
|
1469
1471
|
|
|
1470
1472
|
const defaults = {
|
|
1471
1473
|
brackets: ["(", "[", "{", "'", '"'],
|
|
1472
|
-
before: ")]}:;>"
|
|
1474
|
+
before: ")]}:;>",
|
|
1475
|
+
stringPrefixes: []
|
|
1473
1476
|
};
|
|
1474
1477
|
const closeBracketEffect = /*@__PURE__*/StateEffect.define({
|
|
1475
1478
|
map(value, mapping) {
|
|
@@ -1585,7 +1588,7 @@ function insertBracket(state, bracket) {
|
|
|
1585
1588
|
for (let tok of tokens) {
|
|
1586
1589
|
let closed = closing(codePointAt(tok, 0));
|
|
1587
1590
|
if (bracket == tok)
|
|
1588
|
-
return closed == tok ? handleSame(state, tok, tokens.indexOf(tok + tok + tok) > -1)
|
|
1591
|
+
return closed == tok ? handleSame(state, tok, tokens.indexOf(tok + tok + tok) > -1, conf)
|
|
1589
1592
|
: handleOpen(state, tok, closed, conf.before || defaults.before);
|
|
1590
1593
|
if (bracket == closed && closedBracketAt(state, state.selection.main.from))
|
|
1591
1594
|
return handleClose(state, tok, closed);
|
|
@@ -1640,13 +1643,14 @@ function handleClose(state, _open, close) {
|
|
|
1640
1643
|
}
|
|
1641
1644
|
// Handles cases where the open and close token are the same, and
|
|
1642
1645
|
// possibly triple quotes (as in `"""abc"""`-style quoting).
|
|
1643
|
-
function handleSame(state, token, allowTriple) {
|
|
1646
|
+
function handleSame(state, token, allowTriple, config) {
|
|
1647
|
+
let stringPrefixes = config.stringPrefixes || defaults.stringPrefixes;
|
|
1644
1648
|
let dont = null, changes = state.changeByRange(range => {
|
|
1645
1649
|
if (!range.empty)
|
|
1646
1650
|
return { changes: [{ insert: token, from: range.from }, { insert: token, from: range.to }],
|
|
1647
1651
|
effects: closeBracketEffect.of(range.to + token.length),
|
|
1648
1652
|
range: EditorSelection.range(range.anchor + token.length, range.head + token.length) };
|
|
1649
|
-
let pos = range.head, next = nextChar(state.doc, pos);
|
|
1653
|
+
let pos = range.head, next = nextChar(state.doc, pos), start;
|
|
1650
1654
|
if (next == token) {
|
|
1651
1655
|
if (nodeStart(state, pos)) {
|
|
1652
1656
|
return { changes: { insert: token + token, from: pos },
|
|
@@ -1660,14 +1664,14 @@ function handleSame(state, token, allowTriple) {
|
|
|
1660
1664
|
}
|
|
1661
1665
|
}
|
|
1662
1666
|
else if (allowTriple && state.sliceDoc(pos - 2 * token.length, pos) == token + token &&
|
|
1663
|
-
|
|
1667
|
+
(start = canStartStringAt(state, pos - 2 * token.length, stringPrefixes)) > -1 &&
|
|
1668
|
+
nodeStart(state, start)) {
|
|
1664
1669
|
return { changes: { insert: token + token + token + token, from: pos },
|
|
1665
1670
|
effects: closeBracketEffect.of(pos + token.length),
|
|
1666
1671
|
range: EditorSelection.cursor(pos + token.length) };
|
|
1667
1672
|
}
|
|
1668
1673
|
else if (state.charCategorizer(pos)(next) != CharCategory.Word) {
|
|
1669
|
-
|
|
1670
|
-
if (prev != token && state.charCategorizer(pos)(prev) != CharCategory.Word && !probablyInString(state, pos, token))
|
|
1674
|
+
if (canStartStringAt(state, pos, stringPrefixes) > -1 && !probablyInString(state, pos, token, stringPrefixes))
|
|
1671
1675
|
return { changes: { insert: token + token, from: pos },
|
|
1672
1676
|
effects: closeBracketEffect.of(pos + token.length),
|
|
1673
1677
|
range: EditorSelection.cursor(pos + token.length) };
|
|
@@ -1683,12 +1687,15 @@ function nodeStart(state, pos) {
|
|
|
1683
1687
|
let tree = syntaxTree(state).resolveInner(pos + 1);
|
|
1684
1688
|
return tree.parent && tree.from == pos;
|
|
1685
1689
|
}
|
|
1686
|
-
function probablyInString(state, pos, quoteToken) {
|
|
1690
|
+
function probablyInString(state, pos, quoteToken, prefixes) {
|
|
1687
1691
|
let node = syntaxTree(state).resolveInner(pos, -1);
|
|
1692
|
+
let maxPrefix = prefixes.reduce((m, p) => Math.max(m, p.length), 0);
|
|
1688
1693
|
for (let i = 0; i < 5; i++) {
|
|
1689
|
-
|
|
1694
|
+
let start = state.sliceDoc(node.from, Math.min(node.to, node.from + quoteToken.length + maxPrefix));
|
|
1695
|
+
let quotePos = start.indexOf(quoteToken);
|
|
1696
|
+
if (!quotePos || quotePos > -1 && prefixes.indexOf(start.slice(0, quotePos)) > -1) {
|
|
1690
1697
|
let first = node.firstChild;
|
|
1691
|
-
while (first && first.from == node.from && first.to - first.from > quoteToken.length) {
|
|
1698
|
+
while (first && first.from == node.from && first.to - first.from > quoteToken.length + quotePos) {
|
|
1692
1699
|
if (state.sliceDoc(first.to - quoteToken.length, first.to) == quoteToken)
|
|
1693
1700
|
return false;
|
|
1694
1701
|
first = first.firstChild;
|
|
@@ -1702,6 +1709,17 @@ function probablyInString(state, pos, quoteToken) {
|
|
|
1702
1709
|
}
|
|
1703
1710
|
return false;
|
|
1704
1711
|
}
|
|
1712
|
+
function canStartStringAt(state, pos, prefixes) {
|
|
1713
|
+
let charCat = state.charCategorizer(pos);
|
|
1714
|
+
if (charCat(state.sliceDoc(pos - 1, pos)) != CharCategory.Word)
|
|
1715
|
+
return pos;
|
|
1716
|
+
for (let prefix of prefixes) {
|
|
1717
|
+
let start = pos - prefix.length;
|
|
1718
|
+
if (state.sliceDoc(start, pos) == prefix && charCat(state.sliceDoc(start - 1, start)) != CharCategory.Word)
|
|
1719
|
+
return start;
|
|
1720
|
+
}
|
|
1721
|
+
return -1;
|
|
1722
|
+
}
|
|
1705
1723
|
|
|
1706
1724
|
/**
|
|
1707
1725
|
Returns an extension that enables autocompletion.
|