@codemirror/autocomplete 6.2.0 → 6.3.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 +16 -0
- package/dist/index.cjs +76 -33
- package/dist/index.d.ts +7 -1
- package/dist/index.js +76 -33
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
## 6.3.1 (2022-11-14)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix a regression where transactions for picking a completion (without custom `apply` method) no longer had the `pickedCompletion` annotation.
|
|
6
|
+
|
|
7
|
+
Reduce flickering for completion sources without `validFor` info by temporarily showing a disabled tooltip while the completion updates.
|
|
8
|
+
|
|
9
|
+
Make sure completion info tooltips are kept within the space provided by the `tooltipSpace` option.
|
|
10
|
+
|
|
11
|
+
## 6.3.0 (2022-09-22)
|
|
12
|
+
|
|
13
|
+
### New features
|
|
14
|
+
|
|
15
|
+
Close bracket configuration now supports a `stringPrefixes` property that can be used to allow autoclosing of prefixed strings.
|
|
16
|
+
|
|
1
17
|
## 6.2.0 (2022-09-13)
|
|
2
18
|
|
|
3
19
|
### New features
|
package/dist/index.cjs
CHANGED
|
@@ -179,7 +179,7 @@ function applyCompletion(view, option) {
|
|
|
179
179
|
const apply = option.completion.apply || option.completion.label;
|
|
180
180
|
let result = option.source;
|
|
181
181
|
if (typeof apply == "string")
|
|
182
|
-
view.dispatch(insertCompletionText(view.state, apply, result.from, result.to));
|
|
182
|
+
view.dispatch(Object.assign(Object.assign({}, insertCompletionText(view.state, apply, result.from, result.to)), { annotations: pickedCompletion.of(option.completion) }));
|
|
183
183
|
else
|
|
184
184
|
apply(view, option.completion, result.from, result.to);
|
|
185
185
|
}
|
|
@@ -414,6 +414,7 @@ class CompletionTooltip {
|
|
|
414
414
|
write: (pos) => this.positionInfo(pos),
|
|
415
415
|
key: this
|
|
416
416
|
};
|
|
417
|
+
this.space = null;
|
|
417
418
|
let cState = view.state.field(stateField);
|
|
418
419
|
let { options, selected } = cState.open;
|
|
419
420
|
let config = view.state.facet(completionConfig);
|
|
@@ -439,10 +440,17 @@ class CompletionTooltip {
|
|
|
439
440
|
}
|
|
440
441
|
mount() { this.updateSel(); }
|
|
441
442
|
update(update) {
|
|
442
|
-
|
|
443
|
+
var _a, _b, _c;
|
|
444
|
+
let cState = update.state.field(this.stateField);
|
|
445
|
+
let prevState = update.startState.field(this.stateField);
|
|
446
|
+
if (cState != prevState) {
|
|
443
447
|
this.updateSel();
|
|
448
|
+
if (((_a = cState.open) === null || _a === void 0 ? void 0 : _a.disabled) != ((_b = prevState.open) === null || _b === void 0 ? void 0 : _b.disabled))
|
|
449
|
+
this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!((_c = cState.open) === null || _c === void 0 ? void 0 : _c.disabled));
|
|
450
|
+
}
|
|
444
451
|
}
|
|
445
|
-
positioned() {
|
|
452
|
+
positioned(space) {
|
|
453
|
+
this.space = space;
|
|
446
454
|
if (this.info)
|
|
447
455
|
this.view.requestMeasure(this.placeInfo);
|
|
448
456
|
}
|
|
@@ -509,27 +517,32 @@ class CompletionTooltip {
|
|
|
509
517
|
let sel = this.dom.querySelector("[aria-selected]");
|
|
510
518
|
if (!sel || !this.info)
|
|
511
519
|
return null;
|
|
512
|
-
let win = this.dom.ownerDocument.defaultView;
|
|
513
520
|
let listRect = this.dom.getBoundingClientRect();
|
|
514
521
|
let infoRect = this.info.getBoundingClientRect();
|
|
515
522
|
let selRect = sel.getBoundingClientRect();
|
|
516
|
-
|
|
523
|
+
let space = this.space;
|
|
524
|
+
if (!space) {
|
|
525
|
+
let win = this.dom.ownerDocument.defaultView || window;
|
|
526
|
+
space = { left: 0, top: 0, right: win.innerWidth, bottom: win.innerHeight };
|
|
527
|
+
}
|
|
528
|
+
if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 ||
|
|
529
|
+
selRect.bottom < Math.max(space.top, listRect.top) + 10)
|
|
517
530
|
return null;
|
|
518
531
|
let rtl = this.view.textDirection == view.Direction.RTL, left = rtl, narrow = false, maxWidth;
|
|
519
532
|
let top = "", bottom = "";
|
|
520
|
-
let spaceLeft = listRect.left, spaceRight =
|
|
533
|
+
let spaceLeft = listRect.left - space.left, spaceRight = space.right - listRect.right;
|
|
521
534
|
if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
|
|
522
535
|
left = false;
|
|
523
536
|
else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
|
|
524
537
|
left = true;
|
|
525
538
|
if (infoRect.width <= (left ? spaceLeft : spaceRight)) {
|
|
526
|
-
top = (Math.max(
|
|
539
|
+
top = (Math.max(space.top, Math.min(selRect.top, space.bottom - infoRect.height)) - listRect.top) + "px";
|
|
527
540
|
maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight) + "px";
|
|
528
541
|
}
|
|
529
542
|
else {
|
|
530
543
|
narrow = true;
|
|
531
|
-
maxWidth = Math.min(400 /* Info.Width */, (rtl ? listRect.right :
|
|
532
|
-
let spaceBelow =
|
|
544
|
+
maxWidth = Math.min(400 /* Info.Width */, (rtl ? listRect.right : space.right - listRect.left) - 30 /* Info.Margin */) + "px";
|
|
545
|
+
let spaceBelow = space.bottom - listRect.bottom;
|
|
533
546
|
if (spaceBelow >= infoRect.height || spaceBelow > listRect.top) // Below the completion
|
|
534
547
|
top = (selRect.bottom - listRect.top) + "px";
|
|
535
548
|
else // Above it
|
|
@@ -638,21 +651,24 @@ function sortOptions(active, state) {
|
|
|
638
651
|
return result;
|
|
639
652
|
}
|
|
640
653
|
class CompletionDialog {
|
|
641
|
-
constructor(options, attrs, tooltip, timestamp, selected) {
|
|
654
|
+
constructor(options, attrs, tooltip, timestamp, selected, disabled) {
|
|
642
655
|
this.options = options;
|
|
643
656
|
this.attrs = attrs;
|
|
644
657
|
this.tooltip = tooltip;
|
|
645
658
|
this.timestamp = timestamp;
|
|
646
659
|
this.selected = selected;
|
|
660
|
+
this.disabled = disabled;
|
|
647
661
|
}
|
|
648
662
|
setSelected(selected, id) {
|
|
649
663
|
return selected == this.selected || selected >= this.options.length ? this
|
|
650
|
-
: new CompletionDialog(this.options, makeAttrs(id, selected), this.tooltip, this.timestamp, selected);
|
|
664
|
+
: new CompletionDialog(this.options, makeAttrs(id, selected), this.tooltip, this.timestamp, selected, this.disabled);
|
|
651
665
|
}
|
|
652
666
|
static build(active, state, id, prev, conf) {
|
|
653
667
|
let options = sortOptions(active, state);
|
|
654
|
-
if (!options.length)
|
|
655
|
-
return
|
|
668
|
+
if (!options.length) {
|
|
669
|
+
return prev && active.some(a => a.state == 1 /* State.Pending */) ?
|
|
670
|
+
new CompletionDialog(prev.options, prev.attrs, prev.tooltip, prev.timestamp, prev.selected, true) : null;
|
|
671
|
+
}
|
|
656
672
|
let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
|
|
657
673
|
if (prev && prev.selected != selected && prev.selected != -1) {
|
|
658
674
|
let selectedValue = prev.options[prev.selected].completion;
|
|
@@ -666,10 +682,10 @@ class CompletionDialog {
|
|
|
666
682
|
pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
|
|
667
683
|
create: completionTooltip(completionState),
|
|
668
684
|
above: conf.aboveCursor,
|
|
669
|
-
}, prev ? prev.timestamp : Date.now(), selected);
|
|
685
|
+
}, prev ? prev.timestamp : Date.now(), selected, false);
|
|
670
686
|
}
|
|
671
687
|
map(changes) {
|
|
672
|
-
return new CompletionDialog(this.options, this.attrs, Object.assign(Object.assign({}, this.tooltip), { pos: changes.mapPos(this.tooltip.pos) }), this.timestamp, this.selected);
|
|
688
|
+
return new CompletionDialog(this.options, this.attrs, Object.assign(Object.assign({}, this.tooltip), { pos: changes.mapPos(this.tooltip.pos) }), this.timestamp, this.selected, this.disabled);
|
|
673
689
|
}
|
|
674
690
|
}
|
|
675
691
|
class CompletionState {
|
|
@@ -692,9 +708,12 @@ class CompletionState {
|
|
|
692
708
|
});
|
|
693
709
|
if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
|
|
694
710
|
active = this.active;
|
|
695
|
-
let open =
|
|
696
|
-
|
|
697
|
-
|
|
711
|
+
let open = this.open;
|
|
712
|
+
if (tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
|
|
713
|
+
!sameResults(active, this.active))
|
|
714
|
+
open = CompletionDialog.build(active, state, this.id, this.open, conf);
|
|
715
|
+
else if (open && tr.docChanged)
|
|
716
|
+
open = open.map(tr.changes);
|
|
698
717
|
if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult()))
|
|
699
718
|
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* State.Inactive */) : a);
|
|
700
719
|
for (let effect of tr.effects)
|
|
@@ -834,7 +853,7 @@ backward by the given amount.
|
|
|
834
853
|
function moveCompletionSelection(forward, by = "option") {
|
|
835
854
|
return (view$1) => {
|
|
836
855
|
let cState = view$1.state.field(completionState, false);
|
|
837
|
-
if (!cState || !cState.open ||
|
|
856
|
+
if (!cState || !cState.open || cState.open.disabled ||
|
|
838
857
|
Date.now() - cState.open.timestamp < view$1.state.facet(completionConfig).interactionDelay)
|
|
839
858
|
return false;
|
|
840
859
|
let step = 1, tooltip;
|
|
@@ -859,7 +878,8 @@ const acceptCompletion = (view) => {
|
|
|
859
878
|
if (view.state.readOnly || !cState || !cState.open || cState.open.selected < 0 ||
|
|
860
879
|
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
|
861
880
|
return false;
|
|
862
|
-
|
|
881
|
+
if (!cState.open.disabled)
|
|
882
|
+
applyCompletion(view, cState.open.options[cState.open.selected]);
|
|
863
883
|
return true;
|
|
864
884
|
};
|
|
865
885
|
/**
|
|
@@ -1064,10 +1084,16 @@ const baseTheme = view.EditorView.baseTheme({
|
|
|
1064
1084
|
background: "#17c",
|
|
1065
1085
|
color: "white",
|
|
1066
1086
|
},
|
|
1087
|
+
"&light .cm-tooltip-autocomplete-disabled ul li[aria-selected]": {
|
|
1088
|
+
background: "#777",
|
|
1089
|
+
},
|
|
1067
1090
|
"&dark .cm-tooltip-autocomplete ul li[aria-selected]": {
|
|
1068
1091
|
background: "#347",
|
|
1069
1092
|
color: "white",
|
|
1070
1093
|
},
|
|
1094
|
+
"&dark .cm-tooltip-autocomplete-disabled ul li[aria-selected]": {
|
|
1095
|
+
background: "#444",
|
|
1096
|
+
},
|
|
1071
1097
|
".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after": {
|
|
1072
1098
|
content: '"···"',
|
|
1073
1099
|
opacity: 0.5,
|
|
@@ -1091,6 +1117,7 @@ const baseTheme = view.EditorView.baseTheme({
|
|
|
1091
1117
|
verticalAlign: "text-top",
|
|
1092
1118
|
width: 0,
|
|
1093
1119
|
height: "1.15em",
|
|
1120
|
+
display: "inline-block",
|
|
1094
1121
|
margin: "0 -0.7px -.7em",
|
|
1095
1122
|
borderLeft: "1.4px dotted #888"
|
|
1096
1123
|
},
|
|
@@ -1474,7 +1501,8 @@ const completeAnyWord = context => {
|
|
|
1474
1501
|
|
|
1475
1502
|
const defaults = {
|
|
1476
1503
|
brackets: ["(", "[", "{", "'", '"'],
|
|
1477
|
-
before: ")]}:;>"
|
|
1504
|
+
before: ")]}:;>",
|
|
1505
|
+
stringPrefixes: []
|
|
1478
1506
|
};
|
|
1479
1507
|
const closeBracketEffect = state.StateEffect.define({
|
|
1480
1508
|
map(value, mapping) {
|
|
@@ -1590,7 +1618,7 @@ function insertBracket(state$1, bracket) {
|
|
|
1590
1618
|
for (let tok of tokens) {
|
|
1591
1619
|
let closed = closing(state.codePointAt(tok, 0));
|
|
1592
1620
|
if (bracket == tok)
|
|
1593
|
-
return closed == tok ? handleSame(state$1, tok, tokens.indexOf(tok + tok + tok) > -1)
|
|
1621
|
+
return closed == tok ? handleSame(state$1, tok, tokens.indexOf(tok + tok + tok) > -1, conf)
|
|
1594
1622
|
: handleOpen(state$1, tok, closed, conf.before || defaults.before);
|
|
1595
1623
|
if (bracket == closed && closedBracketAt(state$1, state$1.selection.main.from))
|
|
1596
1624
|
return handleClose(state$1, tok, closed);
|
|
@@ -1645,13 +1673,14 @@ function handleClose(state$1, _open, close) {
|
|
|
1645
1673
|
}
|
|
1646
1674
|
// Handles cases where the open and close token are the same, and
|
|
1647
1675
|
// possibly triple quotes (as in `"""abc"""`-style quoting).
|
|
1648
|
-
function handleSame(state$1, token, allowTriple) {
|
|
1676
|
+
function handleSame(state$1, token, allowTriple, config) {
|
|
1677
|
+
let stringPrefixes = config.stringPrefixes || defaults.stringPrefixes;
|
|
1649
1678
|
let dont = null, changes = state$1.changeByRange(range => {
|
|
1650
1679
|
if (!range.empty)
|
|
1651
1680
|
return { changes: [{ insert: token, from: range.from }, { insert: token, from: range.to }],
|
|
1652
1681
|
effects: closeBracketEffect.of(range.to + token.length),
|
|
1653
1682
|
range: state.EditorSelection.range(range.anchor + token.length, range.head + token.length) };
|
|
1654
|
-
let pos = range.head, next = nextChar(state$1.doc, pos);
|
|
1683
|
+
let pos = range.head, next = nextChar(state$1.doc, pos), start;
|
|
1655
1684
|
if (next == token) {
|
|
1656
1685
|
if (nodeStart(state$1, pos)) {
|
|
1657
1686
|
return { changes: { insert: token + token, from: pos },
|
|
@@ -1665,14 +1694,14 @@ function handleSame(state$1, token, allowTriple) {
|
|
|
1665
1694
|
}
|
|
1666
1695
|
}
|
|
1667
1696
|
else if (allowTriple && state$1.sliceDoc(pos - 2 * token.length, pos) == token + token &&
|
|
1668
|
-
|
|
1697
|
+
(start = canStartStringAt(state$1, pos - 2 * token.length, stringPrefixes)) > -1 &&
|
|
1698
|
+
nodeStart(state$1, start)) {
|
|
1669
1699
|
return { changes: { insert: token + token + token + token, from: pos },
|
|
1670
1700
|
effects: closeBracketEffect.of(pos + token.length),
|
|
1671
1701
|
range: state.EditorSelection.cursor(pos + token.length) };
|
|
1672
1702
|
}
|
|
1673
1703
|
else if (state$1.charCategorizer(pos)(next) != state.CharCategory.Word) {
|
|
1674
|
-
|
|
1675
|
-
if (prev != token && state$1.charCategorizer(pos)(prev) != state.CharCategory.Word && !probablyInString(state$1, pos, token))
|
|
1704
|
+
if (canStartStringAt(state$1, pos, stringPrefixes) > -1 && !probablyInString(state$1, pos, token, stringPrefixes))
|
|
1676
1705
|
return { changes: { insert: token + token, from: pos },
|
|
1677
1706
|
effects: closeBracketEffect.of(pos + token.length),
|
|
1678
1707
|
range: state.EditorSelection.cursor(pos + token.length) };
|
|
@@ -1688,12 +1717,15 @@ function nodeStart(state, pos) {
|
|
|
1688
1717
|
let tree = language.syntaxTree(state).resolveInner(pos + 1);
|
|
1689
1718
|
return tree.parent && tree.from == pos;
|
|
1690
1719
|
}
|
|
1691
|
-
function probablyInString(state, pos, quoteToken) {
|
|
1720
|
+
function probablyInString(state, pos, quoteToken, prefixes) {
|
|
1692
1721
|
let node = language.syntaxTree(state).resolveInner(pos, -1);
|
|
1722
|
+
let maxPrefix = prefixes.reduce((m, p) => Math.max(m, p.length), 0);
|
|
1693
1723
|
for (let i = 0; i < 5; i++) {
|
|
1694
|
-
|
|
1724
|
+
let start = state.sliceDoc(node.from, Math.min(node.to, node.from + quoteToken.length + maxPrefix));
|
|
1725
|
+
let quotePos = start.indexOf(quoteToken);
|
|
1726
|
+
if (!quotePos || quotePos > -1 && prefixes.indexOf(start.slice(0, quotePos)) > -1) {
|
|
1695
1727
|
let first = node.firstChild;
|
|
1696
|
-
while (first && first.from == node.from && first.to - first.from > quoteToken.length) {
|
|
1728
|
+
while (first && first.from == node.from && first.to - first.from > quoteToken.length + quotePos) {
|
|
1697
1729
|
if (state.sliceDoc(first.to - quoteToken.length, first.to) == quoteToken)
|
|
1698
1730
|
return false;
|
|
1699
1731
|
first = first.firstChild;
|
|
@@ -1707,6 +1739,17 @@ function probablyInString(state, pos, quoteToken) {
|
|
|
1707
1739
|
}
|
|
1708
1740
|
return false;
|
|
1709
1741
|
}
|
|
1742
|
+
function canStartStringAt(state$1, pos, prefixes) {
|
|
1743
|
+
let charCat = state$1.charCategorizer(pos);
|
|
1744
|
+
if (charCat(state$1.sliceDoc(pos - 1, pos)) != state.CharCategory.Word)
|
|
1745
|
+
return pos;
|
|
1746
|
+
for (let prefix of prefixes) {
|
|
1747
|
+
let start = pos - prefix.length;
|
|
1748
|
+
if (state$1.sliceDoc(start, pos) == prefix && charCat(state$1.sliceDoc(start - 1, start)) != state.CharCategory.Word)
|
|
1749
|
+
return start;
|
|
1750
|
+
}
|
|
1751
|
+
return -1;
|
|
1752
|
+
}
|
|
1710
1753
|
|
|
1711
1754
|
/**
|
|
1712
1755
|
Returns an extension that enables autocompletion.
|
|
@@ -1759,7 +1802,7 @@ Returns the available completions as an array.
|
|
|
1759
1802
|
function currentCompletions(state) {
|
|
1760
1803
|
var _a;
|
|
1761
1804
|
let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
|
|
1762
|
-
if (!open)
|
|
1805
|
+
if (!open || open.disabled)
|
|
1763
1806
|
return [];
|
|
1764
1807
|
let completions = completionArrayCache.get(open.options);
|
|
1765
1808
|
if (!completions)
|
|
@@ -1772,7 +1815,7 @@ Return the currently selected completion, if any.
|
|
|
1772
1815
|
function selectedCompletion(state) {
|
|
1773
1816
|
var _a;
|
|
1774
1817
|
let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
|
|
1775
|
-
return open && open.selected >= 0 ? open.options[open.selected].completion : null;
|
|
1818
|
+
return open && !open.disabled && open.selected >= 0 ? open.options[open.selected].completion : null;
|
|
1776
1819
|
}
|
|
1777
1820
|
/**
|
|
1778
1821
|
Returns the currently selected position in the active completion
|
|
@@ -1781,7 +1824,7 @@ list, or null if no completions are active.
|
|
|
1781
1824
|
function selectedCompletionIndex(state) {
|
|
1782
1825
|
var _a;
|
|
1783
1826
|
let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
|
|
1784
|
-
return open && open.selected >= 0 ? open.selected : null;
|
|
1827
|
+
return open && !open.disabled && open.selected >= 0 ? open.selected : null;
|
|
1785
1828
|
}
|
|
1786
1829
|
/**
|
|
1787
1830
|
Create an effect that can be attached to a transaction to change
|
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;
|
|
@@ -404,6 +405,11 @@ interface CloseBracketConfig {
|
|
|
404
405
|
whitespace. Defaults to `")]}:;>"`.
|
|
405
406
|
*/
|
|
406
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[];
|
|
407
413
|
}
|
|
408
414
|
/**
|
|
409
415
|
Extension to enable bracket-closing behavior. When a closeable
|
package/dist/index.js
CHANGED
|
@@ -175,7 +175,7 @@ function applyCompletion(view, option) {
|
|
|
175
175
|
const apply = option.completion.apply || option.completion.label;
|
|
176
176
|
let result = option.source;
|
|
177
177
|
if (typeof apply == "string")
|
|
178
|
-
view.dispatch(insertCompletionText(view.state, apply, result.from, result.to));
|
|
178
|
+
view.dispatch(Object.assign(Object.assign({}, insertCompletionText(view.state, apply, result.from, result.to)), { annotations: pickedCompletion.of(option.completion) }));
|
|
179
179
|
else
|
|
180
180
|
apply(view, option.completion, result.from, result.to);
|
|
181
181
|
}
|
|
@@ -410,6 +410,7 @@ class CompletionTooltip {
|
|
|
410
410
|
write: (pos) => this.positionInfo(pos),
|
|
411
411
|
key: this
|
|
412
412
|
};
|
|
413
|
+
this.space = null;
|
|
413
414
|
let cState = view.state.field(stateField);
|
|
414
415
|
let { options, selected } = cState.open;
|
|
415
416
|
let config = view.state.facet(completionConfig);
|
|
@@ -435,10 +436,17 @@ class CompletionTooltip {
|
|
|
435
436
|
}
|
|
436
437
|
mount() { this.updateSel(); }
|
|
437
438
|
update(update) {
|
|
438
|
-
|
|
439
|
+
var _a, _b, _c;
|
|
440
|
+
let cState = update.state.field(this.stateField);
|
|
441
|
+
let prevState = update.startState.field(this.stateField);
|
|
442
|
+
if (cState != prevState) {
|
|
439
443
|
this.updateSel();
|
|
444
|
+
if (((_a = cState.open) === null || _a === void 0 ? void 0 : _a.disabled) != ((_b = prevState.open) === null || _b === void 0 ? void 0 : _b.disabled))
|
|
445
|
+
this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!((_c = cState.open) === null || _c === void 0 ? void 0 : _c.disabled));
|
|
446
|
+
}
|
|
440
447
|
}
|
|
441
|
-
positioned() {
|
|
448
|
+
positioned(space) {
|
|
449
|
+
this.space = space;
|
|
442
450
|
if (this.info)
|
|
443
451
|
this.view.requestMeasure(this.placeInfo);
|
|
444
452
|
}
|
|
@@ -505,27 +513,32 @@ class CompletionTooltip {
|
|
|
505
513
|
let sel = this.dom.querySelector("[aria-selected]");
|
|
506
514
|
if (!sel || !this.info)
|
|
507
515
|
return null;
|
|
508
|
-
let win = this.dom.ownerDocument.defaultView;
|
|
509
516
|
let listRect = this.dom.getBoundingClientRect();
|
|
510
517
|
let infoRect = this.info.getBoundingClientRect();
|
|
511
518
|
let selRect = sel.getBoundingClientRect();
|
|
512
|
-
|
|
519
|
+
let space = this.space;
|
|
520
|
+
if (!space) {
|
|
521
|
+
let win = this.dom.ownerDocument.defaultView || window;
|
|
522
|
+
space = { left: 0, top: 0, right: win.innerWidth, bottom: win.innerHeight };
|
|
523
|
+
}
|
|
524
|
+
if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 ||
|
|
525
|
+
selRect.bottom < Math.max(space.top, listRect.top) + 10)
|
|
513
526
|
return null;
|
|
514
527
|
let rtl = this.view.textDirection == Direction.RTL, left = rtl, narrow = false, maxWidth;
|
|
515
528
|
let top = "", bottom = "";
|
|
516
|
-
let spaceLeft = listRect.left, spaceRight =
|
|
529
|
+
let spaceLeft = listRect.left - space.left, spaceRight = space.right - listRect.right;
|
|
517
530
|
if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
|
|
518
531
|
left = false;
|
|
519
532
|
else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
|
|
520
533
|
left = true;
|
|
521
534
|
if (infoRect.width <= (left ? spaceLeft : spaceRight)) {
|
|
522
|
-
top = (Math.max(
|
|
535
|
+
top = (Math.max(space.top, Math.min(selRect.top, space.bottom - infoRect.height)) - listRect.top) + "px";
|
|
523
536
|
maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight) + "px";
|
|
524
537
|
}
|
|
525
538
|
else {
|
|
526
539
|
narrow = true;
|
|
527
|
-
maxWidth = Math.min(400 /* Info.Width */, (rtl ? listRect.right :
|
|
528
|
-
let spaceBelow =
|
|
540
|
+
maxWidth = Math.min(400 /* Info.Width */, (rtl ? listRect.right : space.right - listRect.left) - 30 /* Info.Margin */) + "px";
|
|
541
|
+
let spaceBelow = space.bottom - listRect.bottom;
|
|
529
542
|
if (spaceBelow >= infoRect.height || spaceBelow > listRect.top) // Below the completion
|
|
530
543
|
top = (selRect.bottom - listRect.top) + "px";
|
|
531
544
|
else // Above it
|
|
@@ -634,21 +647,24 @@ function sortOptions(active, state) {
|
|
|
634
647
|
return result;
|
|
635
648
|
}
|
|
636
649
|
class CompletionDialog {
|
|
637
|
-
constructor(options, attrs, tooltip, timestamp, selected) {
|
|
650
|
+
constructor(options, attrs, tooltip, timestamp, selected, disabled) {
|
|
638
651
|
this.options = options;
|
|
639
652
|
this.attrs = attrs;
|
|
640
653
|
this.tooltip = tooltip;
|
|
641
654
|
this.timestamp = timestamp;
|
|
642
655
|
this.selected = selected;
|
|
656
|
+
this.disabled = disabled;
|
|
643
657
|
}
|
|
644
658
|
setSelected(selected, id) {
|
|
645
659
|
return selected == this.selected || selected >= this.options.length ? this
|
|
646
|
-
: new CompletionDialog(this.options, makeAttrs(id, selected), this.tooltip, this.timestamp, selected);
|
|
660
|
+
: new CompletionDialog(this.options, makeAttrs(id, selected), this.tooltip, this.timestamp, selected, this.disabled);
|
|
647
661
|
}
|
|
648
662
|
static build(active, state, id, prev, conf) {
|
|
649
663
|
let options = sortOptions(active, state);
|
|
650
|
-
if (!options.length)
|
|
651
|
-
return
|
|
664
|
+
if (!options.length) {
|
|
665
|
+
return prev && active.some(a => a.state == 1 /* State.Pending */) ?
|
|
666
|
+
new CompletionDialog(prev.options, prev.attrs, prev.tooltip, prev.timestamp, prev.selected, true) : null;
|
|
667
|
+
}
|
|
652
668
|
let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
|
|
653
669
|
if (prev && prev.selected != selected && prev.selected != -1) {
|
|
654
670
|
let selectedValue = prev.options[prev.selected].completion;
|
|
@@ -662,10 +678,10 @@ class CompletionDialog {
|
|
|
662
678
|
pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
|
|
663
679
|
create: completionTooltip(completionState),
|
|
664
680
|
above: conf.aboveCursor,
|
|
665
|
-
}, prev ? prev.timestamp : Date.now(), selected);
|
|
681
|
+
}, prev ? prev.timestamp : Date.now(), selected, false);
|
|
666
682
|
}
|
|
667
683
|
map(changes) {
|
|
668
|
-
return new CompletionDialog(this.options, this.attrs, Object.assign(Object.assign({}, this.tooltip), { pos: changes.mapPos(this.tooltip.pos) }), this.timestamp, this.selected);
|
|
684
|
+
return new CompletionDialog(this.options, this.attrs, Object.assign(Object.assign({}, this.tooltip), { pos: changes.mapPos(this.tooltip.pos) }), this.timestamp, this.selected, this.disabled);
|
|
669
685
|
}
|
|
670
686
|
}
|
|
671
687
|
class CompletionState {
|
|
@@ -688,9 +704,12 @@ class CompletionState {
|
|
|
688
704
|
});
|
|
689
705
|
if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
|
|
690
706
|
active = this.active;
|
|
691
|
-
let open =
|
|
692
|
-
|
|
693
|
-
|
|
707
|
+
let open = this.open;
|
|
708
|
+
if (tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
|
|
709
|
+
!sameResults(active, this.active))
|
|
710
|
+
open = CompletionDialog.build(active, state, this.id, this.open, conf);
|
|
711
|
+
else if (open && tr.docChanged)
|
|
712
|
+
open = open.map(tr.changes);
|
|
694
713
|
if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult()))
|
|
695
714
|
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* State.Inactive */) : a);
|
|
696
715
|
for (let effect of tr.effects)
|
|
@@ -830,7 +849,7 @@ backward by the given amount.
|
|
|
830
849
|
function moveCompletionSelection(forward, by = "option") {
|
|
831
850
|
return (view) => {
|
|
832
851
|
let cState = view.state.field(completionState, false);
|
|
833
|
-
if (!cState || !cState.open ||
|
|
852
|
+
if (!cState || !cState.open || cState.open.disabled ||
|
|
834
853
|
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
|
835
854
|
return false;
|
|
836
855
|
let step = 1, tooltip;
|
|
@@ -855,7 +874,8 @@ const acceptCompletion = (view) => {
|
|
|
855
874
|
if (view.state.readOnly || !cState || !cState.open || cState.open.selected < 0 ||
|
|
856
875
|
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
|
857
876
|
return false;
|
|
858
|
-
|
|
877
|
+
if (!cState.open.disabled)
|
|
878
|
+
applyCompletion(view, cState.open.options[cState.open.selected]);
|
|
859
879
|
return true;
|
|
860
880
|
};
|
|
861
881
|
/**
|
|
@@ -1060,10 +1080,16 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
|
|
|
1060
1080
|
background: "#17c",
|
|
1061
1081
|
color: "white",
|
|
1062
1082
|
},
|
|
1083
|
+
"&light .cm-tooltip-autocomplete-disabled ul li[aria-selected]": {
|
|
1084
|
+
background: "#777",
|
|
1085
|
+
},
|
|
1063
1086
|
"&dark .cm-tooltip-autocomplete ul li[aria-selected]": {
|
|
1064
1087
|
background: "#347",
|
|
1065
1088
|
color: "white",
|
|
1066
1089
|
},
|
|
1090
|
+
"&dark .cm-tooltip-autocomplete-disabled ul li[aria-selected]": {
|
|
1091
|
+
background: "#444",
|
|
1092
|
+
},
|
|
1067
1093
|
".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after": {
|
|
1068
1094
|
content: '"···"',
|
|
1069
1095
|
opacity: 0.5,
|
|
@@ -1087,6 +1113,7 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
|
|
|
1087
1113
|
verticalAlign: "text-top",
|
|
1088
1114
|
width: 0,
|
|
1089
1115
|
height: "1.15em",
|
|
1116
|
+
display: "inline-block",
|
|
1090
1117
|
margin: "0 -0.7px -.7em",
|
|
1091
1118
|
borderLeft: "1.4px dotted #888"
|
|
1092
1119
|
},
|
|
@@ -1470,7 +1497,8 @@ const completeAnyWord = context => {
|
|
|
1470
1497
|
|
|
1471
1498
|
const defaults = {
|
|
1472
1499
|
brackets: ["(", "[", "{", "'", '"'],
|
|
1473
|
-
before: ")]}:;>"
|
|
1500
|
+
before: ")]}:;>",
|
|
1501
|
+
stringPrefixes: []
|
|
1474
1502
|
};
|
|
1475
1503
|
const closeBracketEffect = /*@__PURE__*/StateEffect.define({
|
|
1476
1504
|
map(value, mapping) {
|
|
@@ -1586,7 +1614,7 @@ function insertBracket(state, bracket) {
|
|
|
1586
1614
|
for (let tok of tokens) {
|
|
1587
1615
|
let closed = closing(codePointAt(tok, 0));
|
|
1588
1616
|
if (bracket == tok)
|
|
1589
|
-
return closed == tok ? handleSame(state, tok, tokens.indexOf(tok + tok + tok) > -1)
|
|
1617
|
+
return closed == tok ? handleSame(state, tok, tokens.indexOf(tok + tok + tok) > -1, conf)
|
|
1590
1618
|
: handleOpen(state, tok, closed, conf.before || defaults.before);
|
|
1591
1619
|
if (bracket == closed && closedBracketAt(state, state.selection.main.from))
|
|
1592
1620
|
return handleClose(state, tok, closed);
|
|
@@ -1641,13 +1669,14 @@ function handleClose(state, _open, close) {
|
|
|
1641
1669
|
}
|
|
1642
1670
|
// Handles cases where the open and close token are the same, and
|
|
1643
1671
|
// possibly triple quotes (as in `"""abc"""`-style quoting).
|
|
1644
|
-
function handleSame(state, token, allowTriple) {
|
|
1672
|
+
function handleSame(state, token, allowTriple, config) {
|
|
1673
|
+
let stringPrefixes = config.stringPrefixes || defaults.stringPrefixes;
|
|
1645
1674
|
let dont = null, changes = state.changeByRange(range => {
|
|
1646
1675
|
if (!range.empty)
|
|
1647
1676
|
return { changes: [{ insert: token, from: range.from }, { insert: token, from: range.to }],
|
|
1648
1677
|
effects: closeBracketEffect.of(range.to + token.length),
|
|
1649
1678
|
range: EditorSelection.range(range.anchor + token.length, range.head + token.length) };
|
|
1650
|
-
let pos = range.head, next = nextChar(state.doc, pos);
|
|
1679
|
+
let pos = range.head, next = nextChar(state.doc, pos), start;
|
|
1651
1680
|
if (next == token) {
|
|
1652
1681
|
if (nodeStart(state, pos)) {
|
|
1653
1682
|
return { changes: { insert: token + token, from: pos },
|
|
@@ -1661,14 +1690,14 @@ function handleSame(state, token, allowTriple) {
|
|
|
1661
1690
|
}
|
|
1662
1691
|
}
|
|
1663
1692
|
else if (allowTriple && state.sliceDoc(pos - 2 * token.length, pos) == token + token &&
|
|
1664
|
-
|
|
1693
|
+
(start = canStartStringAt(state, pos - 2 * token.length, stringPrefixes)) > -1 &&
|
|
1694
|
+
nodeStart(state, start)) {
|
|
1665
1695
|
return { changes: { insert: token + token + token + token, from: pos },
|
|
1666
1696
|
effects: closeBracketEffect.of(pos + token.length),
|
|
1667
1697
|
range: EditorSelection.cursor(pos + token.length) };
|
|
1668
1698
|
}
|
|
1669
1699
|
else if (state.charCategorizer(pos)(next) != CharCategory.Word) {
|
|
1670
|
-
|
|
1671
|
-
if (prev != token && state.charCategorizer(pos)(prev) != CharCategory.Word && !probablyInString(state, pos, token))
|
|
1700
|
+
if (canStartStringAt(state, pos, stringPrefixes) > -1 && !probablyInString(state, pos, token, stringPrefixes))
|
|
1672
1701
|
return { changes: { insert: token + token, from: pos },
|
|
1673
1702
|
effects: closeBracketEffect.of(pos + token.length),
|
|
1674
1703
|
range: EditorSelection.cursor(pos + token.length) };
|
|
@@ -1684,12 +1713,15 @@ function nodeStart(state, pos) {
|
|
|
1684
1713
|
let tree = syntaxTree(state).resolveInner(pos + 1);
|
|
1685
1714
|
return tree.parent && tree.from == pos;
|
|
1686
1715
|
}
|
|
1687
|
-
function probablyInString(state, pos, quoteToken) {
|
|
1716
|
+
function probablyInString(state, pos, quoteToken, prefixes) {
|
|
1688
1717
|
let node = syntaxTree(state).resolveInner(pos, -1);
|
|
1718
|
+
let maxPrefix = prefixes.reduce((m, p) => Math.max(m, p.length), 0);
|
|
1689
1719
|
for (let i = 0; i < 5; i++) {
|
|
1690
|
-
|
|
1720
|
+
let start = state.sliceDoc(node.from, Math.min(node.to, node.from + quoteToken.length + maxPrefix));
|
|
1721
|
+
let quotePos = start.indexOf(quoteToken);
|
|
1722
|
+
if (!quotePos || quotePos > -1 && prefixes.indexOf(start.slice(0, quotePos)) > -1) {
|
|
1691
1723
|
let first = node.firstChild;
|
|
1692
|
-
while (first && first.from == node.from && first.to - first.from > quoteToken.length) {
|
|
1724
|
+
while (first && first.from == node.from && first.to - first.from > quoteToken.length + quotePos) {
|
|
1693
1725
|
if (state.sliceDoc(first.to - quoteToken.length, first.to) == quoteToken)
|
|
1694
1726
|
return false;
|
|
1695
1727
|
first = first.firstChild;
|
|
@@ -1703,6 +1735,17 @@ function probablyInString(state, pos, quoteToken) {
|
|
|
1703
1735
|
}
|
|
1704
1736
|
return false;
|
|
1705
1737
|
}
|
|
1738
|
+
function canStartStringAt(state, pos, prefixes) {
|
|
1739
|
+
let charCat = state.charCategorizer(pos);
|
|
1740
|
+
if (charCat(state.sliceDoc(pos - 1, pos)) != CharCategory.Word)
|
|
1741
|
+
return pos;
|
|
1742
|
+
for (let prefix of prefixes) {
|
|
1743
|
+
let start = pos - prefix.length;
|
|
1744
|
+
if (state.sliceDoc(start, pos) == prefix && charCat(state.sliceDoc(start - 1, start)) != CharCategory.Word)
|
|
1745
|
+
return start;
|
|
1746
|
+
}
|
|
1747
|
+
return -1;
|
|
1748
|
+
}
|
|
1706
1749
|
|
|
1707
1750
|
/**
|
|
1708
1751
|
Returns an extension that enables autocompletion.
|
|
@@ -1755,7 +1798,7 @@ Returns the available completions as an array.
|
|
|
1755
1798
|
function currentCompletions(state) {
|
|
1756
1799
|
var _a;
|
|
1757
1800
|
let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
|
|
1758
|
-
if (!open)
|
|
1801
|
+
if (!open || open.disabled)
|
|
1759
1802
|
return [];
|
|
1760
1803
|
let completions = completionArrayCache.get(open.options);
|
|
1761
1804
|
if (!completions)
|
|
@@ -1768,7 +1811,7 @@ Return the currently selected completion, if any.
|
|
|
1768
1811
|
function selectedCompletion(state) {
|
|
1769
1812
|
var _a;
|
|
1770
1813
|
let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
|
|
1771
|
-
return open && open.selected >= 0 ? open.options[open.selected].completion : null;
|
|
1814
|
+
return open && !open.disabled && open.selected >= 0 ? open.options[open.selected].completion : null;
|
|
1772
1815
|
}
|
|
1773
1816
|
/**
|
|
1774
1817
|
Returns the currently selected position in the active completion
|
|
@@ -1777,7 +1820,7 @@ list, or null if no completions are active.
|
|
|
1777
1820
|
function selectedCompletionIndex(state) {
|
|
1778
1821
|
var _a;
|
|
1779
1822
|
let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
|
|
1780
|
-
return open && open.selected >= 0 ? open.selected : null;
|
|
1823
|
+
return open && !open.disabled && open.selected >= 0 ? open.selected : null;
|
|
1781
1824
|
}
|
|
1782
1825
|
/**
|
|
1783
1826
|
Create an effect that can be attached to a transaction to change
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codemirror/autocomplete",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.1",
|
|
4
4
|
"description": "Autocompletion for the CodeMirror code editor",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "cm-runtests",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@codemirror/language": "^6.0.0",
|
|
30
30
|
"@codemirror/state": "^6.0.0",
|
|
31
|
-
"@codemirror/view": "^6.
|
|
31
|
+
"@codemirror/view": "^6.5.0",
|
|
32
32
|
"@lezer/common": "^1.0.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|