@codemirror/autocomplete 6.4.2 → 6.5.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 +20 -0
- package/dist/index.cjs +134 -79
- package/dist/index.d.ts +48 -2
- package/dist/index.js +136 -81
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
## 6.5.1 (2023-04-13)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Keep completions open when interaction with an info tooltip moves focus out of the editor.
|
|
6
|
+
|
|
7
|
+
## 6.5.0 (2023-04-13)
|
|
8
|
+
|
|
9
|
+
### Bug fixes
|
|
10
|
+
|
|
11
|
+
When `closeBrackets` skips a bracket, it now generates a change that overwrites the bracket.
|
|
12
|
+
|
|
13
|
+
Replace the entire selected range when picking a completion with a non-cursor selection active.
|
|
14
|
+
|
|
15
|
+
### New features
|
|
16
|
+
|
|
17
|
+
Completions can now provide a `section` field that is used to group them into sections.
|
|
18
|
+
|
|
19
|
+
The new `positionInfo` option can be used to provide custom logic for positioning the info tooltips.
|
|
20
|
+
|
|
1
21
|
## 6.4.2 (2023-02-17)
|
|
2
22
|
|
|
3
23
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -137,13 +137,14 @@ function ifNotIn(nodes, source) {
|
|
|
137
137
|
};
|
|
138
138
|
}
|
|
139
139
|
class Option {
|
|
140
|
-
constructor(completion, source, match) {
|
|
140
|
+
constructor(completion, source, match, score) {
|
|
141
141
|
this.completion = completion;
|
|
142
142
|
this.source = source;
|
|
143
143
|
this.match = match;
|
|
144
|
+
this.score = score;
|
|
144
145
|
}
|
|
145
146
|
}
|
|
146
|
-
function cur(state) { return state.selection.main.
|
|
147
|
+
function cur(state) { return state.selection.main.from; }
|
|
147
148
|
// Make sure the given regexp has a $ at its end and, if `start` is
|
|
148
149
|
// true, a ^ at its start.
|
|
149
150
|
function ensureAnchor(expr, start) {
|
|
@@ -165,18 +166,13 @@ completion's text in the main selection range, and any other
|
|
|
165
166
|
selection range that has the same text in front of it.
|
|
166
167
|
*/
|
|
167
168
|
function insertCompletionText(state$1, text, from, to) {
|
|
169
|
+
let { main } = state$1.selection, len = to - from;
|
|
168
170
|
return Object.assign(Object.assign({}, state$1.changeByRange(range => {
|
|
169
|
-
if (range
|
|
170
|
-
|
|
171
|
-
changes: { from: from, to: to, insert: text },
|
|
172
|
-
range: state.EditorSelection.cursor(from + text.length)
|
|
173
|
-
};
|
|
174
|
-
let len = to - from;
|
|
175
|
-
if (!range.empty ||
|
|
176
|
-
len && state$1.sliceDoc(range.from - len, range.from) != state$1.sliceDoc(from, to))
|
|
171
|
+
if (range != main && len &&
|
|
172
|
+
state$1.sliceDoc(range.from - len, range.from + to - main.from) != state$1.sliceDoc(from, to))
|
|
177
173
|
return { range };
|
|
178
174
|
return {
|
|
179
|
-
changes: { from: range.from - len, to: range.from, insert: text },
|
|
175
|
+
changes: { from: range.from - len, to: to == main.from ? range.to : range.from + to - main.from, insert: text },
|
|
180
176
|
range: state.EditorSelection.cursor(range.from - len + text.length)
|
|
181
177
|
};
|
|
182
178
|
})), { userEvent: "input.complete" });
|
|
@@ -198,6 +194,8 @@ function asSource(source) {
|
|
|
198
194
|
SourceCache.set(source, known = completeFromList(source));
|
|
199
195
|
return known;
|
|
200
196
|
}
|
|
197
|
+
const startCompletionEffect = state.StateEffect.define();
|
|
198
|
+
const closeCompletionEffect = state.StateEffect.define();
|
|
201
199
|
|
|
202
200
|
// A pattern matcher for fuzzy completion matching. Create an instance
|
|
203
201
|
// once for a pattern, and then use that to match any number of
|
|
@@ -343,6 +341,7 @@ const completionConfig = state.Facet.define({
|
|
|
343
341
|
aboveCursor: false,
|
|
344
342
|
icons: true,
|
|
345
343
|
addToOptions: [],
|
|
344
|
+
positionInfo: defaultPositionInfo,
|
|
346
345
|
compareCompletions: (a, b) => a.label.localeCompare(b.label),
|
|
347
346
|
interactionDelay: 75
|
|
348
347
|
}, {
|
|
@@ -358,6 +357,36 @@ const completionConfig = state.Facet.define({
|
|
|
358
357
|
function joinClass(a, b) {
|
|
359
358
|
return a ? b ? a + " " + b : a : b;
|
|
360
359
|
}
|
|
360
|
+
function defaultPositionInfo(view$1, list, option, info, space) {
|
|
361
|
+
let rtl = view$1.textDirection == view.Direction.RTL, left = rtl, narrow = false;
|
|
362
|
+
let side = "top", offset, maxWidth;
|
|
363
|
+
let spaceLeft = list.left - space.left, spaceRight = space.right - list.right;
|
|
364
|
+
let infoWidth = info.right - info.left, infoHeight = info.bottom - info.top;
|
|
365
|
+
if (left && spaceLeft < Math.min(infoWidth, spaceRight))
|
|
366
|
+
left = false;
|
|
367
|
+
else if (!left && spaceRight < Math.min(infoWidth, spaceLeft))
|
|
368
|
+
left = true;
|
|
369
|
+
if (infoWidth <= (left ? spaceLeft : spaceRight)) {
|
|
370
|
+
offset = Math.max(space.top, Math.min(option.top, space.bottom - infoHeight)) - list.top;
|
|
371
|
+
maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight);
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
narrow = true;
|
|
375
|
+
maxWidth = Math.min(400 /* Info.Width */, (rtl ? list.right : space.right - list.left) - 30 /* Info.Margin */);
|
|
376
|
+
let spaceBelow = space.bottom - list.bottom;
|
|
377
|
+
if (spaceBelow >= infoHeight || spaceBelow > list.top) { // Below the completion
|
|
378
|
+
offset = option.bottom - list.top;
|
|
379
|
+
}
|
|
380
|
+
else { // Above it
|
|
381
|
+
side = "bottom";
|
|
382
|
+
offset = list.bottom - option.top;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return {
|
|
386
|
+
style: `${side}: ${offset}px; max-width: ${maxWidth}px`,
|
|
387
|
+
class: "cm-completionInfo-" + (narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right")
|
|
388
|
+
};
|
|
389
|
+
}
|
|
361
390
|
|
|
362
391
|
function optionContent(config) {
|
|
363
392
|
let content = config.addToOptions.slice();
|
|
@@ -422,9 +451,9 @@ class CompletionTooltip {
|
|
|
422
451
|
this.view = view;
|
|
423
452
|
this.stateField = stateField;
|
|
424
453
|
this.info = null;
|
|
425
|
-
this.
|
|
454
|
+
this.placeInfoReq = {
|
|
426
455
|
read: () => this.measureInfo(),
|
|
427
|
-
write: (pos) => this.
|
|
456
|
+
write: (pos) => this.placeInfo(pos),
|
|
428
457
|
key: this
|
|
429
458
|
};
|
|
430
459
|
this.space = null;
|
|
@@ -448,10 +477,16 @@ class CompletionTooltip {
|
|
|
448
477
|
}
|
|
449
478
|
}
|
|
450
479
|
});
|
|
480
|
+
this.dom.addEventListener("focusout", (e) => {
|
|
481
|
+
let state = view.state.field(this.stateField, false);
|
|
482
|
+
if (state && state.tooltip && view.state.facet(completionConfig).closeOnBlur &&
|
|
483
|
+
e.relatedTarget != view.contentDOM)
|
|
484
|
+
view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
485
|
+
});
|
|
451
486
|
this.list = this.dom.appendChild(this.createListBox(options, cState.id, this.range));
|
|
452
487
|
this.list.addEventListener("scroll", () => {
|
|
453
488
|
if (this.info)
|
|
454
|
-
this.view.requestMeasure(this.
|
|
489
|
+
this.view.requestMeasure(this.placeInfoReq);
|
|
455
490
|
});
|
|
456
491
|
}
|
|
457
492
|
mount() { this.updateSel(); }
|
|
@@ -481,7 +516,7 @@ class CompletionTooltip {
|
|
|
481
516
|
positioned(space) {
|
|
482
517
|
this.space = space;
|
|
483
518
|
if (this.info)
|
|
484
|
-
this.view.requestMeasure(this.
|
|
519
|
+
this.view.requestMeasure(this.placeInfoReq);
|
|
485
520
|
}
|
|
486
521
|
updateSel() {
|
|
487
522
|
let cState = this.view.state.field(this.stateField), open = cState.open;
|
|
@@ -491,7 +526,7 @@ class CompletionTooltip {
|
|
|
491
526
|
this.list = this.dom.appendChild(this.createListBox(open.options, cState.id, this.range));
|
|
492
527
|
this.list.addEventListener("scroll", () => {
|
|
493
528
|
if (this.info)
|
|
494
|
-
this.view.requestMeasure(this.
|
|
529
|
+
this.view.requestMeasure(this.placeInfoReq);
|
|
495
530
|
});
|
|
496
531
|
}
|
|
497
532
|
if (this.updateSelectedOption(open.selected)) {
|
|
@@ -522,12 +557,15 @@ class CompletionTooltip {
|
|
|
522
557
|
dom.className = "cm-tooltip cm-completionInfo";
|
|
523
558
|
dom.appendChild(content);
|
|
524
559
|
this.dom.appendChild(dom);
|
|
525
|
-
this.view.requestMeasure(this.
|
|
560
|
+
this.view.requestMeasure(this.placeInfoReq);
|
|
526
561
|
}
|
|
527
562
|
updateSelectedOption(selected) {
|
|
528
563
|
let set = null;
|
|
529
564
|
for (let opt = this.list.firstChild, i = this.range.from; opt; opt = opt.nextSibling, i++) {
|
|
530
|
-
if (
|
|
565
|
+
if (opt.nodeName != "LI" || !opt.id) {
|
|
566
|
+
i--; // A section header
|
|
567
|
+
}
|
|
568
|
+
else if (i == selected) {
|
|
531
569
|
if (!opt.hasAttribute("aria-selected")) {
|
|
532
570
|
opt.setAttribute("aria-selected", "true");
|
|
533
571
|
set = opt;
|
|
@@ -557,41 +595,17 @@ class CompletionTooltip {
|
|
|
557
595
|
if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 ||
|
|
558
596
|
selRect.bottom < Math.max(space.top, listRect.top) + 10)
|
|
559
597
|
return null;
|
|
560
|
-
|
|
561
|
-
let top = "", bottom = "";
|
|
562
|
-
let spaceLeft = listRect.left - space.left, spaceRight = space.right - listRect.right;
|
|
563
|
-
if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
|
|
564
|
-
left = false;
|
|
565
|
-
else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
|
|
566
|
-
left = true;
|
|
567
|
-
if (infoRect.width <= (left ? spaceLeft : spaceRight)) {
|
|
568
|
-
top = (Math.max(space.top, Math.min(selRect.top, space.bottom - infoRect.height)) - listRect.top) + "px";
|
|
569
|
-
maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight) + "px";
|
|
570
|
-
}
|
|
571
|
-
else {
|
|
572
|
-
narrow = true;
|
|
573
|
-
maxWidth = Math.min(400 /* Info.Width */, (rtl ? listRect.right : space.right - listRect.left) - 30 /* Info.Margin */) + "px";
|
|
574
|
-
let spaceBelow = space.bottom - listRect.bottom;
|
|
575
|
-
if (spaceBelow >= infoRect.height || spaceBelow > listRect.top) // Below the completion
|
|
576
|
-
top = (selRect.bottom - listRect.top) + "px";
|
|
577
|
-
else // Above it
|
|
578
|
-
bottom = (listRect.bottom - selRect.top) + "px";
|
|
579
|
-
}
|
|
580
|
-
return {
|
|
581
|
-
top, bottom, maxWidth,
|
|
582
|
-
class: narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right",
|
|
583
|
-
};
|
|
598
|
+
return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space);
|
|
584
599
|
}
|
|
585
|
-
|
|
600
|
+
placeInfo(pos) {
|
|
586
601
|
if (this.info) {
|
|
587
602
|
if (pos) {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
this.info.
|
|
591
|
-
this.info.className = "cm-tooltip cm-completionInfo cm-completionInfo-" + pos.class;
|
|
603
|
+
if (pos.style)
|
|
604
|
+
this.info.style.cssText = pos.style;
|
|
605
|
+
this.info.className = "cm-tooltip cm-completionInfo " + (pos.class || "");
|
|
592
606
|
}
|
|
593
607
|
else {
|
|
594
|
-
this.info.style.
|
|
608
|
+
this.info.style.cssText = "top: -1e6px";
|
|
595
609
|
}
|
|
596
610
|
}
|
|
597
611
|
}
|
|
@@ -601,8 +615,22 @@ class CompletionTooltip {
|
|
|
601
615
|
ul.setAttribute("role", "listbox");
|
|
602
616
|
ul.setAttribute("aria-expanded", "true");
|
|
603
617
|
ul.setAttribute("aria-label", this.view.state.phrase("Completions"));
|
|
618
|
+
let curSection = null;
|
|
604
619
|
for (let i = range.from; i < range.to; i++) {
|
|
605
|
-
let { completion, match } = options[i];
|
|
620
|
+
let { completion, match } = options[i], { section } = completion;
|
|
621
|
+
if (section) {
|
|
622
|
+
let name = typeof section == "string" ? section : section.name;
|
|
623
|
+
if (name != curSection && (i > range.from || range.from == 0)) {
|
|
624
|
+
curSection = name;
|
|
625
|
+
if (typeof section != "string" && section.header) {
|
|
626
|
+
ul.appendChild(section.header(section));
|
|
627
|
+
}
|
|
628
|
+
else {
|
|
629
|
+
let header = ul.appendChild(document.createElement("completion-section"));
|
|
630
|
+
header.textContent = name;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
606
634
|
const li = ul.appendChild(document.createElement("li"));
|
|
607
635
|
li.id = id + "-" + i;
|
|
608
636
|
li.setAttribute("role", "option");
|
|
@@ -643,32 +671,55 @@ function score(option) {
|
|
|
643
671
|
(option.type ? 1 : 0);
|
|
644
672
|
}
|
|
645
673
|
function sortOptions(active, state) {
|
|
646
|
-
let options = []
|
|
674
|
+
let options = [];
|
|
675
|
+
let sections = null;
|
|
676
|
+
let addOption = (option) => {
|
|
677
|
+
options.push(option);
|
|
678
|
+
let { section } = option.completion;
|
|
679
|
+
if (section) {
|
|
680
|
+
if (!sections)
|
|
681
|
+
sections = [];
|
|
682
|
+
let name = typeof section == "string" ? section : section.name;
|
|
683
|
+
if (!sections.some(s => s.name == name))
|
|
684
|
+
sections.push(typeof section == "string" ? { name } : section);
|
|
685
|
+
}
|
|
686
|
+
};
|
|
647
687
|
for (let a of active)
|
|
648
688
|
if (a.hasResult()) {
|
|
649
689
|
if (a.result.filter === false) {
|
|
650
690
|
let getMatch = a.result.getMatch;
|
|
651
691
|
for (let option of a.result.options) {
|
|
652
|
-
let match = [1e9 -
|
|
692
|
+
let match = [1e9 - options.length];
|
|
653
693
|
if (getMatch)
|
|
654
694
|
for (let n of getMatch(option))
|
|
655
695
|
match.push(n);
|
|
656
|
-
|
|
696
|
+
addOption(new Option(option, a, match, match[0]));
|
|
657
697
|
}
|
|
658
698
|
}
|
|
659
699
|
else {
|
|
660
700
|
let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match;
|
|
661
701
|
for (let option of a.result.options)
|
|
662
702
|
if (match = matcher.match(option.label)) {
|
|
663
|
-
|
|
664
|
-
match[0] += option.boost;
|
|
665
|
-
options.push(new Option(option, a, match));
|
|
703
|
+
addOption(new Option(option, a, match, match[0] + (option.boost || 0)));
|
|
666
704
|
}
|
|
667
705
|
}
|
|
668
706
|
}
|
|
707
|
+
if (sections) {
|
|
708
|
+
let sectionOrder = Object.create(null), pos = 0;
|
|
709
|
+
let cmp = (a, b) => { var _a, _b; return ((_a = a.rank) !== null && _a !== void 0 ? _a : 1e9) - ((_b = b.rank) !== null && _b !== void 0 ? _b : 1e9) || (a.name < b.name ? -1 : 1); };
|
|
710
|
+
for (let s of sections.sort(cmp)) {
|
|
711
|
+
pos -= 1e5;
|
|
712
|
+
sectionOrder[s.name] = pos;
|
|
713
|
+
}
|
|
714
|
+
for (let option of options) {
|
|
715
|
+
let { section } = option.completion;
|
|
716
|
+
if (section)
|
|
717
|
+
option.score += sectionOrder[typeof section == "string" ? section : section.name];
|
|
718
|
+
}
|
|
719
|
+
}
|
|
669
720
|
let result = [], prev = null;
|
|
670
721
|
let compare = state.facet(completionConfig).compareCompletions;
|
|
671
|
-
for (let opt of options.sort((a, b) => (b.
|
|
722
|
+
for (let opt of options.sort((a, b) => (b.score - a.score) || compare(a.completion, b.completion))) {
|
|
672
723
|
if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail ||
|
|
673
724
|
(prev.type != null && opt.completion.type != null && prev.type != opt.completion.type) ||
|
|
674
725
|
prev.apply != opt.completion.apply)
|
|
@@ -862,8 +913,6 @@ function checkValid(validFor, state, from, to) {
|
|
|
862
913
|
let text = state.sliceDoc(from, to);
|
|
863
914
|
return typeof validFor == "function" ? validFor(text, from, to, state) : ensureAnchor(validFor, true).test(text);
|
|
864
915
|
}
|
|
865
|
-
const startCompletionEffect = state.StateEffect.define();
|
|
866
|
-
const closeCompletionEffect = state.StateEffect.define();
|
|
867
916
|
const setActiveEffect = state.StateEffect.define({
|
|
868
917
|
map(sources, mapping) { return sources.map(s => s.map(mapping)); }
|
|
869
918
|
});
|
|
@@ -1070,10 +1119,13 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
|
|
|
1070
1119
|
}
|
|
1071
1120
|
}, {
|
|
1072
1121
|
eventHandlers: {
|
|
1073
|
-
blur() {
|
|
1122
|
+
blur(event) {
|
|
1074
1123
|
let state = this.view.state.field(completionState, false);
|
|
1075
|
-
if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur)
|
|
1076
|
-
|
|
1124
|
+
if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur) {
|
|
1125
|
+
let dialog = state.open && view.getTooltip(this.view, state.open.tooltip);
|
|
1126
|
+
if (!dialog || !dialog.dom.contains(event.relatedTarget))
|
|
1127
|
+
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
1128
|
+
}
|
|
1077
1129
|
},
|
|
1078
1130
|
compositionstart() {
|
|
1079
1131
|
this.composing = 1 /* CompositionState.Started */;
|
|
@@ -1103,13 +1155,21 @@ const baseTheme = view.EditorView.baseTheme({
|
|
|
1103
1155
|
listStyle: "none",
|
|
1104
1156
|
margin: 0,
|
|
1105
1157
|
padding: 0,
|
|
1158
|
+
"& > li, & > completion-section": {
|
|
1159
|
+
padding: "1px 3px",
|
|
1160
|
+
lineHeight: 1.2
|
|
1161
|
+
},
|
|
1106
1162
|
"& > li": {
|
|
1107
1163
|
overflowX: "hidden",
|
|
1108
1164
|
textOverflow: "ellipsis",
|
|
1109
|
-
cursor: "pointer"
|
|
1110
|
-
padding: "1px 3px",
|
|
1111
|
-
lineHeight: 1.2
|
|
1165
|
+
cursor: "pointer"
|
|
1112
1166
|
},
|
|
1167
|
+
"& > completion-section": {
|
|
1168
|
+
display: "list-item",
|
|
1169
|
+
borderBottom: "1px solid silver",
|
|
1170
|
+
paddingLeft: "0.5em",
|
|
1171
|
+
opacity: 0.7
|
|
1172
|
+
}
|
|
1113
1173
|
}
|
|
1114
1174
|
},
|
|
1115
1175
|
"&light .cm-tooltip-autocomplete ul li[aria-selected]": {
|
|
@@ -1543,9 +1603,6 @@ const closeBracketEffect = state.StateEffect.define({
|
|
|
1543
1603
|
return mapped == null ? undefined : mapped;
|
|
1544
1604
|
}
|
|
1545
1605
|
});
|
|
1546
|
-
const skipBracketEffect = state.StateEffect.define({
|
|
1547
|
-
map(value, mapping) { return mapping.mapPos(value); }
|
|
1548
|
-
});
|
|
1549
1606
|
const closedBracket = new class extends state.RangeValue {
|
|
1550
1607
|
};
|
|
1551
1608
|
closedBracket.startSide = 1;
|
|
@@ -1560,12 +1617,9 @@ const bracketState = state.StateField.define({
|
|
|
1560
1617
|
value = state.RangeSet.empty;
|
|
1561
1618
|
}
|
|
1562
1619
|
value = value.map(tr.changes);
|
|
1563
|
-
for (let effect of tr.effects)
|
|
1620
|
+
for (let effect of tr.effects)
|
|
1564
1621
|
if (effect.is(closeBracketEffect))
|
|
1565
1622
|
value = value.update({ add: [closedBracket.range(effect.value, effect.value + 1)] });
|
|
1566
|
-
else if (effect.is(skipBracketEffect))
|
|
1567
|
-
value = value.update({ filter: from => from != effect.value });
|
|
1568
|
-
}
|
|
1569
1623
|
return value;
|
|
1570
1624
|
}
|
|
1571
1625
|
});
|
|
@@ -1693,15 +1747,15 @@ function handleOpen(state$1, open, close, closeBefore) {
|
|
|
1693
1747
|
});
|
|
1694
1748
|
}
|
|
1695
1749
|
function handleClose(state$1, _open, close) {
|
|
1696
|
-
let dont = null,
|
|
1750
|
+
let dont = null, changes = state$1.changeByRange(range => {
|
|
1697
1751
|
if (range.empty && nextChar(state$1.doc, range.head) == close)
|
|
1698
|
-
return
|
|
1699
|
-
|
|
1752
|
+
return { changes: { from: range.head, to: range.head + close.length, insert: close },
|
|
1753
|
+
range: state.EditorSelection.cursor(range.head + close.length) };
|
|
1754
|
+
return dont = { range };
|
|
1700
1755
|
});
|
|
1701
|
-
return dont ? null : state$1.update({
|
|
1702
|
-
selection: state.EditorSelection.create(moved, state$1.selection.mainIndex),
|
|
1756
|
+
return dont ? null : state$1.update(changes, {
|
|
1703
1757
|
scrollIntoView: true,
|
|
1704
|
-
|
|
1758
|
+
userEvent: "input.type"
|
|
1705
1759
|
});
|
|
1706
1760
|
}
|
|
1707
1761
|
// Handles cases where the open and close token are the same, and
|
|
@@ -1722,8 +1776,9 @@ function handleSame(state$1, token, allowTriple, config) {
|
|
|
1722
1776
|
}
|
|
1723
1777
|
else if (closedBracketAt(state$1, pos)) {
|
|
1724
1778
|
let isTriple = allowTriple && state$1.sliceDoc(pos, pos + token.length * 3) == token + token + token;
|
|
1725
|
-
|
|
1726
|
-
|
|
1779
|
+
let content = isTriple ? token + token + token : token;
|
|
1780
|
+
return { changes: { from: pos, to: pos + content.length, insert: content },
|
|
1781
|
+
range: state.EditorSelection.cursor(pos + content.length) };
|
|
1727
1782
|
}
|
|
1728
1783
|
}
|
|
1729
1784
|
else if (allowTriple && state$1.sliceDoc(pos - 2 * token.length, pos) == token + token &&
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as _codemirror_state from '@codemirror/state';
|
|
2
2
|
import { EditorState, TransactionSpec, Transaction, StateCommand, Facet, Extension, StateEffect } from '@codemirror/state';
|
|
3
|
-
import { EditorView, KeyBinding, Command } from '@codemirror/view';
|
|
3
|
+
import { EditorView, Rect, KeyBinding, Command } from '@codemirror/view';
|
|
4
4
|
import * as _lezer_common from '@lezer/common';
|
|
5
5
|
|
|
6
6
|
interface CompletionConfig {
|
|
@@ -79,6 +79,19 @@ interface CompletionConfig {
|
|
|
79
79
|
position: number;
|
|
80
80
|
}[];
|
|
81
81
|
/**
|
|
82
|
+
By default, [info](https://codemirror.net/6/docs/ref/#autocomplet.Completion.info) tooltips are
|
|
83
|
+
placed to the side of the selected. This option can be used to
|
|
84
|
+
override that. It will be given rectangles for the list of
|
|
85
|
+
completions, the selected option, the info element, and the
|
|
86
|
+
availble [tooltip space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace),
|
|
87
|
+
and should return style and/or class strings for the info
|
|
88
|
+
element.
|
|
89
|
+
*/
|
|
90
|
+
positionInfo?: (view: EditorView, list: Rect, option: Rect, info: Rect, space: Rect) => {
|
|
91
|
+
style?: string;
|
|
92
|
+
class?: string;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
82
95
|
The comparison function to use when sorting completions with the same
|
|
83
96
|
match score. Defaults to using
|
|
84
97
|
[`localeCompare`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
|
|
@@ -143,6 +156,39 @@ interface Completion {
|
|
|
143
156
|
down the list, a positive number moves it up.
|
|
144
157
|
*/
|
|
145
158
|
boost?: number;
|
|
159
|
+
/**
|
|
160
|
+
Can be used to divide the completion list into sections.
|
|
161
|
+
Completions in a given section (matched by name) will be grouped
|
|
162
|
+
together, with a heading above them. Options without section
|
|
163
|
+
will appear above all sections. A string value is equivalent to
|
|
164
|
+
a `{name}` object.
|
|
165
|
+
*/
|
|
166
|
+
section?: string | CompletionSection;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
Object used to describe a completion
|
|
170
|
+
[section](https://codemirror.net/6/docs/ref/#autocomplete.Completion.section). It is recommended to
|
|
171
|
+
create a shared object used by all the completions in a given
|
|
172
|
+
section.
|
|
173
|
+
*/
|
|
174
|
+
interface CompletionSection {
|
|
175
|
+
/**
|
|
176
|
+
The name of the section. If no `render` method is present, this
|
|
177
|
+
will be displayed above the options.
|
|
178
|
+
*/
|
|
179
|
+
name: string;
|
|
180
|
+
/**
|
|
181
|
+
An optional function that renders the section header. Since the
|
|
182
|
+
headers are shown inside a list, you should make sure the
|
|
183
|
+
resulting element has a `display: list-item` style.
|
|
184
|
+
*/
|
|
185
|
+
header?: (section: CompletionSection) => HTMLElement;
|
|
186
|
+
/**
|
|
187
|
+
By default, sections are ordered alphabetically by name. To
|
|
188
|
+
specify an explicit order, `rank` can be used. Sections with a
|
|
189
|
+
lower rank will be shown above sections with a higher rank.
|
|
190
|
+
*/
|
|
191
|
+
rank?: number;
|
|
146
192
|
}
|
|
147
193
|
/**
|
|
148
194
|
An instance of this is passed to completion source functions.
|
|
@@ -488,4 +534,4 @@ the currently selected completion.
|
|
|
488
534
|
*/
|
|
489
535
|
declare function setSelectedCompletion(index: number): StateEffect<unknown>;
|
|
490
536
|
|
|
491
|
-
export { CloseBracketConfig, Completion, CompletionContext, CompletionResult, CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeBrackets, closeBracketsKeymap, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, deleteBracketPair, ifIn, ifNotIn, insertBracket, insertCompletionText, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, selectedCompletionIndex, setSelectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };
|
|
537
|
+
export { CloseBracketConfig, Completion, CompletionContext, CompletionResult, CompletionSection, CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeBrackets, closeBracketsKeymap, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, deleteBracketPair, ifIn, ifNotIn, insertBracket, insertCompletionText, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, selectedCompletionIndex, setSelectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Annotation, EditorSelection, codePointAt, codePointSize, fromCodePoint, Facet, combineConfig,
|
|
2
|
-
import {
|
|
1
|
+
import { Annotation, StateEffect, EditorSelection, codePointAt, codePointSize, fromCodePoint, Facet, combineConfig, StateField, Prec, Text, MapMode, RangeValue, RangeSet, CharCategory } from '@codemirror/state';
|
|
2
|
+
import { Direction, logException, showTooltip, EditorView, ViewPlugin, getTooltip, Decoration, WidgetType, keymap } from '@codemirror/view';
|
|
3
3
|
import { syntaxTree, indentUnit } from '@codemirror/language';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -133,13 +133,14 @@ function ifNotIn(nodes, source) {
|
|
|
133
133
|
};
|
|
134
134
|
}
|
|
135
135
|
class Option {
|
|
136
|
-
constructor(completion, source, match) {
|
|
136
|
+
constructor(completion, source, match, score) {
|
|
137
137
|
this.completion = completion;
|
|
138
138
|
this.source = source;
|
|
139
139
|
this.match = match;
|
|
140
|
+
this.score = score;
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
|
-
function cur(state) { return state.selection.main.
|
|
143
|
+
function cur(state) { return state.selection.main.from; }
|
|
143
144
|
// Make sure the given regexp has a $ at its end and, if `start` is
|
|
144
145
|
// true, a ^ at its start.
|
|
145
146
|
function ensureAnchor(expr, start) {
|
|
@@ -161,18 +162,13 @@ completion's text in the main selection range, and any other
|
|
|
161
162
|
selection range that has the same text in front of it.
|
|
162
163
|
*/
|
|
163
164
|
function insertCompletionText(state, text, from, to) {
|
|
165
|
+
let { main } = state.selection, len = to - from;
|
|
164
166
|
return Object.assign(Object.assign({}, state.changeByRange(range => {
|
|
165
|
-
if (range
|
|
166
|
-
|
|
167
|
-
changes: { from: from, to: to, insert: text },
|
|
168
|
-
range: EditorSelection.cursor(from + text.length)
|
|
169
|
-
};
|
|
170
|
-
let len = to - from;
|
|
171
|
-
if (!range.empty ||
|
|
172
|
-
len && state.sliceDoc(range.from - len, range.from) != state.sliceDoc(from, to))
|
|
167
|
+
if (range != main && len &&
|
|
168
|
+
state.sliceDoc(range.from - len, range.from + to - main.from) != state.sliceDoc(from, to))
|
|
173
169
|
return { range };
|
|
174
170
|
return {
|
|
175
|
-
changes: { from: range.from - len, to: range.from, insert: text },
|
|
171
|
+
changes: { from: range.from - len, to: to == main.from ? range.to : range.from + to - main.from, insert: text },
|
|
176
172
|
range: EditorSelection.cursor(range.from - len + text.length)
|
|
177
173
|
};
|
|
178
174
|
})), { userEvent: "input.complete" });
|
|
@@ -194,6 +190,8 @@ function asSource(source) {
|
|
|
194
190
|
SourceCache.set(source, known = completeFromList(source));
|
|
195
191
|
return known;
|
|
196
192
|
}
|
|
193
|
+
const startCompletionEffect = /*@__PURE__*/StateEffect.define();
|
|
194
|
+
const closeCompletionEffect = /*@__PURE__*/StateEffect.define();
|
|
197
195
|
|
|
198
196
|
// A pattern matcher for fuzzy completion matching. Create an instance
|
|
199
197
|
// once for a pattern, and then use that to match any number of
|
|
@@ -339,6 +337,7 @@ const completionConfig = /*@__PURE__*/Facet.define({
|
|
|
339
337
|
aboveCursor: false,
|
|
340
338
|
icons: true,
|
|
341
339
|
addToOptions: [],
|
|
340
|
+
positionInfo: defaultPositionInfo,
|
|
342
341
|
compareCompletions: (a, b) => a.label.localeCompare(b.label),
|
|
343
342
|
interactionDelay: 75
|
|
344
343
|
}, {
|
|
@@ -354,6 +353,36 @@ const completionConfig = /*@__PURE__*/Facet.define({
|
|
|
354
353
|
function joinClass(a, b) {
|
|
355
354
|
return a ? b ? a + " " + b : a : b;
|
|
356
355
|
}
|
|
356
|
+
function defaultPositionInfo(view, list, option, info, space) {
|
|
357
|
+
let rtl = view.textDirection == Direction.RTL, left = rtl, narrow = false;
|
|
358
|
+
let side = "top", offset, maxWidth;
|
|
359
|
+
let spaceLeft = list.left - space.left, spaceRight = space.right - list.right;
|
|
360
|
+
let infoWidth = info.right - info.left, infoHeight = info.bottom - info.top;
|
|
361
|
+
if (left && spaceLeft < Math.min(infoWidth, spaceRight))
|
|
362
|
+
left = false;
|
|
363
|
+
else if (!left && spaceRight < Math.min(infoWidth, spaceLeft))
|
|
364
|
+
left = true;
|
|
365
|
+
if (infoWidth <= (left ? spaceLeft : spaceRight)) {
|
|
366
|
+
offset = Math.max(space.top, Math.min(option.top, space.bottom - infoHeight)) - list.top;
|
|
367
|
+
maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight);
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
narrow = true;
|
|
371
|
+
maxWidth = Math.min(400 /* Info.Width */, (rtl ? list.right : space.right - list.left) - 30 /* Info.Margin */);
|
|
372
|
+
let spaceBelow = space.bottom - list.bottom;
|
|
373
|
+
if (spaceBelow >= infoHeight || spaceBelow > list.top) { // Below the completion
|
|
374
|
+
offset = option.bottom - list.top;
|
|
375
|
+
}
|
|
376
|
+
else { // Above it
|
|
377
|
+
side = "bottom";
|
|
378
|
+
offset = list.bottom - option.top;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
return {
|
|
382
|
+
style: `${side}: ${offset}px; max-width: ${maxWidth}px`,
|
|
383
|
+
class: "cm-completionInfo-" + (narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right")
|
|
384
|
+
};
|
|
385
|
+
}
|
|
357
386
|
|
|
358
387
|
function optionContent(config) {
|
|
359
388
|
let content = config.addToOptions.slice();
|
|
@@ -418,9 +447,9 @@ class CompletionTooltip {
|
|
|
418
447
|
this.view = view;
|
|
419
448
|
this.stateField = stateField;
|
|
420
449
|
this.info = null;
|
|
421
|
-
this.
|
|
450
|
+
this.placeInfoReq = {
|
|
422
451
|
read: () => this.measureInfo(),
|
|
423
|
-
write: (pos) => this.
|
|
452
|
+
write: (pos) => this.placeInfo(pos),
|
|
424
453
|
key: this
|
|
425
454
|
};
|
|
426
455
|
this.space = null;
|
|
@@ -444,10 +473,16 @@ class CompletionTooltip {
|
|
|
444
473
|
}
|
|
445
474
|
}
|
|
446
475
|
});
|
|
476
|
+
this.dom.addEventListener("focusout", (e) => {
|
|
477
|
+
let state = view.state.field(this.stateField, false);
|
|
478
|
+
if (state && state.tooltip && view.state.facet(completionConfig).closeOnBlur &&
|
|
479
|
+
e.relatedTarget != view.contentDOM)
|
|
480
|
+
view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
481
|
+
});
|
|
447
482
|
this.list = this.dom.appendChild(this.createListBox(options, cState.id, this.range));
|
|
448
483
|
this.list.addEventListener("scroll", () => {
|
|
449
484
|
if (this.info)
|
|
450
|
-
this.view.requestMeasure(this.
|
|
485
|
+
this.view.requestMeasure(this.placeInfoReq);
|
|
451
486
|
});
|
|
452
487
|
}
|
|
453
488
|
mount() { this.updateSel(); }
|
|
@@ -477,7 +512,7 @@ class CompletionTooltip {
|
|
|
477
512
|
positioned(space) {
|
|
478
513
|
this.space = space;
|
|
479
514
|
if (this.info)
|
|
480
|
-
this.view.requestMeasure(this.
|
|
515
|
+
this.view.requestMeasure(this.placeInfoReq);
|
|
481
516
|
}
|
|
482
517
|
updateSel() {
|
|
483
518
|
let cState = this.view.state.field(this.stateField), open = cState.open;
|
|
@@ -487,7 +522,7 @@ class CompletionTooltip {
|
|
|
487
522
|
this.list = this.dom.appendChild(this.createListBox(open.options, cState.id, this.range));
|
|
488
523
|
this.list.addEventListener("scroll", () => {
|
|
489
524
|
if (this.info)
|
|
490
|
-
this.view.requestMeasure(this.
|
|
525
|
+
this.view.requestMeasure(this.placeInfoReq);
|
|
491
526
|
});
|
|
492
527
|
}
|
|
493
528
|
if (this.updateSelectedOption(open.selected)) {
|
|
@@ -518,12 +553,15 @@ class CompletionTooltip {
|
|
|
518
553
|
dom.className = "cm-tooltip cm-completionInfo";
|
|
519
554
|
dom.appendChild(content);
|
|
520
555
|
this.dom.appendChild(dom);
|
|
521
|
-
this.view.requestMeasure(this.
|
|
556
|
+
this.view.requestMeasure(this.placeInfoReq);
|
|
522
557
|
}
|
|
523
558
|
updateSelectedOption(selected) {
|
|
524
559
|
let set = null;
|
|
525
560
|
for (let opt = this.list.firstChild, i = this.range.from; opt; opt = opt.nextSibling, i++) {
|
|
526
|
-
if (
|
|
561
|
+
if (opt.nodeName != "LI" || !opt.id) {
|
|
562
|
+
i--; // A section header
|
|
563
|
+
}
|
|
564
|
+
else if (i == selected) {
|
|
527
565
|
if (!opt.hasAttribute("aria-selected")) {
|
|
528
566
|
opt.setAttribute("aria-selected", "true");
|
|
529
567
|
set = opt;
|
|
@@ -553,41 +591,17 @@ class CompletionTooltip {
|
|
|
553
591
|
if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 ||
|
|
554
592
|
selRect.bottom < Math.max(space.top, listRect.top) + 10)
|
|
555
593
|
return null;
|
|
556
|
-
|
|
557
|
-
let top = "", bottom = "";
|
|
558
|
-
let spaceLeft = listRect.left - space.left, spaceRight = space.right - listRect.right;
|
|
559
|
-
if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
|
|
560
|
-
left = false;
|
|
561
|
-
else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
|
|
562
|
-
left = true;
|
|
563
|
-
if (infoRect.width <= (left ? spaceLeft : spaceRight)) {
|
|
564
|
-
top = (Math.max(space.top, Math.min(selRect.top, space.bottom - infoRect.height)) - listRect.top) + "px";
|
|
565
|
-
maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight) + "px";
|
|
566
|
-
}
|
|
567
|
-
else {
|
|
568
|
-
narrow = true;
|
|
569
|
-
maxWidth = Math.min(400 /* Info.Width */, (rtl ? listRect.right : space.right - listRect.left) - 30 /* Info.Margin */) + "px";
|
|
570
|
-
let spaceBelow = space.bottom - listRect.bottom;
|
|
571
|
-
if (spaceBelow >= infoRect.height || spaceBelow > listRect.top) // Below the completion
|
|
572
|
-
top = (selRect.bottom - listRect.top) + "px";
|
|
573
|
-
else // Above it
|
|
574
|
-
bottom = (listRect.bottom - selRect.top) + "px";
|
|
575
|
-
}
|
|
576
|
-
return {
|
|
577
|
-
top, bottom, maxWidth,
|
|
578
|
-
class: narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right",
|
|
579
|
-
};
|
|
594
|
+
return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space);
|
|
580
595
|
}
|
|
581
|
-
|
|
596
|
+
placeInfo(pos) {
|
|
582
597
|
if (this.info) {
|
|
583
598
|
if (pos) {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
this.info.
|
|
587
|
-
this.info.className = "cm-tooltip cm-completionInfo cm-completionInfo-" + pos.class;
|
|
599
|
+
if (pos.style)
|
|
600
|
+
this.info.style.cssText = pos.style;
|
|
601
|
+
this.info.className = "cm-tooltip cm-completionInfo " + (pos.class || "");
|
|
588
602
|
}
|
|
589
603
|
else {
|
|
590
|
-
this.info.style.
|
|
604
|
+
this.info.style.cssText = "top: -1e6px";
|
|
591
605
|
}
|
|
592
606
|
}
|
|
593
607
|
}
|
|
@@ -597,8 +611,22 @@ class CompletionTooltip {
|
|
|
597
611
|
ul.setAttribute("role", "listbox");
|
|
598
612
|
ul.setAttribute("aria-expanded", "true");
|
|
599
613
|
ul.setAttribute("aria-label", this.view.state.phrase("Completions"));
|
|
614
|
+
let curSection = null;
|
|
600
615
|
for (let i = range.from; i < range.to; i++) {
|
|
601
|
-
let { completion, match } = options[i];
|
|
616
|
+
let { completion, match } = options[i], { section } = completion;
|
|
617
|
+
if (section) {
|
|
618
|
+
let name = typeof section == "string" ? section : section.name;
|
|
619
|
+
if (name != curSection && (i > range.from || range.from == 0)) {
|
|
620
|
+
curSection = name;
|
|
621
|
+
if (typeof section != "string" && section.header) {
|
|
622
|
+
ul.appendChild(section.header(section));
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
let header = ul.appendChild(document.createElement("completion-section"));
|
|
626
|
+
header.textContent = name;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
602
630
|
const li = ul.appendChild(document.createElement("li"));
|
|
603
631
|
li.id = id + "-" + i;
|
|
604
632
|
li.setAttribute("role", "option");
|
|
@@ -639,32 +667,55 @@ function score(option) {
|
|
|
639
667
|
(option.type ? 1 : 0);
|
|
640
668
|
}
|
|
641
669
|
function sortOptions(active, state) {
|
|
642
|
-
let options = []
|
|
670
|
+
let options = [];
|
|
671
|
+
let sections = null;
|
|
672
|
+
let addOption = (option) => {
|
|
673
|
+
options.push(option);
|
|
674
|
+
let { section } = option.completion;
|
|
675
|
+
if (section) {
|
|
676
|
+
if (!sections)
|
|
677
|
+
sections = [];
|
|
678
|
+
let name = typeof section == "string" ? section : section.name;
|
|
679
|
+
if (!sections.some(s => s.name == name))
|
|
680
|
+
sections.push(typeof section == "string" ? { name } : section);
|
|
681
|
+
}
|
|
682
|
+
};
|
|
643
683
|
for (let a of active)
|
|
644
684
|
if (a.hasResult()) {
|
|
645
685
|
if (a.result.filter === false) {
|
|
646
686
|
let getMatch = a.result.getMatch;
|
|
647
687
|
for (let option of a.result.options) {
|
|
648
|
-
let match = [1e9 -
|
|
688
|
+
let match = [1e9 - options.length];
|
|
649
689
|
if (getMatch)
|
|
650
690
|
for (let n of getMatch(option))
|
|
651
691
|
match.push(n);
|
|
652
|
-
|
|
692
|
+
addOption(new Option(option, a, match, match[0]));
|
|
653
693
|
}
|
|
654
694
|
}
|
|
655
695
|
else {
|
|
656
696
|
let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match;
|
|
657
697
|
for (let option of a.result.options)
|
|
658
698
|
if (match = matcher.match(option.label)) {
|
|
659
|
-
|
|
660
|
-
match[0] += option.boost;
|
|
661
|
-
options.push(new Option(option, a, match));
|
|
699
|
+
addOption(new Option(option, a, match, match[0] + (option.boost || 0)));
|
|
662
700
|
}
|
|
663
701
|
}
|
|
664
702
|
}
|
|
703
|
+
if (sections) {
|
|
704
|
+
let sectionOrder = Object.create(null), pos = 0;
|
|
705
|
+
let cmp = (a, b) => { var _a, _b; return ((_a = a.rank) !== null && _a !== void 0 ? _a : 1e9) - ((_b = b.rank) !== null && _b !== void 0 ? _b : 1e9) || (a.name < b.name ? -1 : 1); };
|
|
706
|
+
for (let s of sections.sort(cmp)) {
|
|
707
|
+
pos -= 1e5;
|
|
708
|
+
sectionOrder[s.name] = pos;
|
|
709
|
+
}
|
|
710
|
+
for (let option of options) {
|
|
711
|
+
let { section } = option.completion;
|
|
712
|
+
if (section)
|
|
713
|
+
option.score += sectionOrder[typeof section == "string" ? section : section.name];
|
|
714
|
+
}
|
|
715
|
+
}
|
|
665
716
|
let result = [], prev = null;
|
|
666
717
|
let compare = state.facet(completionConfig).compareCompletions;
|
|
667
|
-
for (let opt of options.sort((a, b) => (b.
|
|
718
|
+
for (let opt of options.sort((a, b) => (b.score - a.score) || compare(a.completion, b.completion))) {
|
|
668
719
|
if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail ||
|
|
669
720
|
(prev.type != null && opt.completion.type != null && prev.type != opt.completion.type) ||
|
|
670
721
|
prev.apply != opt.completion.apply)
|
|
@@ -858,8 +909,6 @@ function checkValid(validFor, state, from, to) {
|
|
|
858
909
|
let text = state.sliceDoc(from, to);
|
|
859
910
|
return typeof validFor == "function" ? validFor(text, from, to, state) : ensureAnchor(validFor, true).test(text);
|
|
860
911
|
}
|
|
861
|
-
const startCompletionEffect = /*@__PURE__*/StateEffect.define();
|
|
862
|
-
const closeCompletionEffect = /*@__PURE__*/StateEffect.define();
|
|
863
912
|
const setActiveEffect = /*@__PURE__*/StateEffect.define({
|
|
864
913
|
map(sources, mapping) { return sources.map(s => s.map(mapping)); }
|
|
865
914
|
});
|
|
@@ -1066,10 +1115,13 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
|
|
|
1066
1115
|
}
|
|
1067
1116
|
}, {
|
|
1068
1117
|
eventHandlers: {
|
|
1069
|
-
blur() {
|
|
1118
|
+
blur(event) {
|
|
1070
1119
|
let state = this.view.state.field(completionState, false);
|
|
1071
|
-
if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur)
|
|
1072
|
-
this.view
|
|
1120
|
+
if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur) {
|
|
1121
|
+
let dialog = state.open && getTooltip(this.view, state.open.tooltip);
|
|
1122
|
+
if (!dialog || !dialog.dom.contains(event.relatedTarget))
|
|
1123
|
+
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
|
|
1124
|
+
}
|
|
1073
1125
|
},
|
|
1074
1126
|
compositionstart() {
|
|
1075
1127
|
this.composing = 1 /* CompositionState.Started */;
|
|
@@ -1099,13 +1151,21 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
|
|
|
1099
1151
|
listStyle: "none",
|
|
1100
1152
|
margin: 0,
|
|
1101
1153
|
padding: 0,
|
|
1154
|
+
"& > li, & > completion-section": {
|
|
1155
|
+
padding: "1px 3px",
|
|
1156
|
+
lineHeight: 1.2
|
|
1157
|
+
},
|
|
1102
1158
|
"& > li": {
|
|
1103
1159
|
overflowX: "hidden",
|
|
1104
1160
|
textOverflow: "ellipsis",
|
|
1105
|
-
cursor: "pointer"
|
|
1106
|
-
padding: "1px 3px",
|
|
1107
|
-
lineHeight: 1.2
|
|
1161
|
+
cursor: "pointer"
|
|
1108
1162
|
},
|
|
1163
|
+
"& > completion-section": {
|
|
1164
|
+
display: "list-item",
|
|
1165
|
+
borderBottom: "1px solid silver",
|
|
1166
|
+
paddingLeft: "0.5em",
|
|
1167
|
+
opacity: 0.7
|
|
1168
|
+
}
|
|
1109
1169
|
}
|
|
1110
1170
|
},
|
|
1111
1171
|
"&light .cm-tooltip-autocomplete ul li[aria-selected]": {
|
|
@@ -1539,9 +1599,6 @@ const closeBracketEffect = /*@__PURE__*/StateEffect.define({
|
|
|
1539
1599
|
return mapped == null ? undefined : mapped;
|
|
1540
1600
|
}
|
|
1541
1601
|
});
|
|
1542
|
-
const skipBracketEffect = /*@__PURE__*/StateEffect.define({
|
|
1543
|
-
map(value, mapping) { return mapping.mapPos(value); }
|
|
1544
|
-
});
|
|
1545
1602
|
const closedBracket = /*@__PURE__*/new class extends RangeValue {
|
|
1546
1603
|
};
|
|
1547
1604
|
closedBracket.startSide = 1;
|
|
@@ -1556,12 +1613,9 @@ const bracketState = /*@__PURE__*/StateField.define({
|
|
|
1556
1613
|
value = RangeSet.empty;
|
|
1557
1614
|
}
|
|
1558
1615
|
value = value.map(tr.changes);
|
|
1559
|
-
for (let effect of tr.effects)
|
|
1616
|
+
for (let effect of tr.effects)
|
|
1560
1617
|
if (effect.is(closeBracketEffect))
|
|
1561
1618
|
value = value.update({ add: [closedBracket.range(effect.value, effect.value + 1)] });
|
|
1562
|
-
else if (effect.is(skipBracketEffect))
|
|
1563
|
-
value = value.update({ filter: from => from != effect.value });
|
|
1564
|
-
}
|
|
1565
1619
|
return value;
|
|
1566
1620
|
}
|
|
1567
1621
|
});
|
|
@@ -1689,15 +1743,15 @@ function handleOpen(state, open, close, closeBefore) {
|
|
|
1689
1743
|
});
|
|
1690
1744
|
}
|
|
1691
1745
|
function handleClose(state, _open, close) {
|
|
1692
|
-
let dont = null,
|
|
1746
|
+
let dont = null, changes = state.changeByRange(range => {
|
|
1693
1747
|
if (range.empty && nextChar(state.doc, range.head) == close)
|
|
1694
|
-
return
|
|
1695
|
-
|
|
1748
|
+
return { changes: { from: range.head, to: range.head + close.length, insert: close },
|
|
1749
|
+
range: EditorSelection.cursor(range.head + close.length) };
|
|
1750
|
+
return dont = { range };
|
|
1696
1751
|
});
|
|
1697
|
-
return dont ? null : state.update({
|
|
1698
|
-
selection: EditorSelection.create(moved, state.selection.mainIndex),
|
|
1752
|
+
return dont ? null : state.update(changes, {
|
|
1699
1753
|
scrollIntoView: true,
|
|
1700
|
-
|
|
1754
|
+
userEvent: "input.type"
|
|
1701
1755
|
});
|
|
1702
1756
|
}
|
|
1703
1757
|
// Handles cases where the open and close token are the same, and
|
|
@@ -1718,8 +1772,9 @@ function handleSame(state, token, allowTriple, config) {
|
|
|
1718
1772
|
}
|
|
1719
1773
|
else if (closedBracketAt(state, pos)) {
|
|
1720
1774
|
let isTriple = allowTriple && state.sliceDoc(pos, pos + token.length * 3) == token + token + token;
|
|
1721
|
-
|
|
1722
|
-
|
|
1775
|
+
let content = isTriple ? token + token + token : token;
|
|
1776
|
+
return { changes: { from: pos, to: pos + content.length, insert: content },
|
|
1777
|
+
range: EditorSelection.cursor(pos + content.length) };
|
|
1723
1778
|
}
|
|
1724
1779
|
}
|
|
1725
1780
|
else if (allowTriple && state.sliceDoc(pos - 2 * token.length, pos) == token + token &&
|