@codemirror/autocomplete 0.19.6 → 0.19.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/dist/index.cjs +38 -24
- package/dist/index.d.ts +6 -0
- package/dist/index.js +38 -24
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
## 0.19.10 (2022-01-05)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Make sure the info tooltip is hidden when the selected option is scrolled out of view.
|
|
6
|
+
|
|
7
|
+
Fix a bug in the completion ranking that would sometimes give options that match the input by word start chars higher scores than appropriate.
|
|
8
|
+
|
|
9
|
+
Options are now sorted (ascending) by length when their match score is otherwise identical.
|
|
10
|
+
|
|
11
|
+
## 0.19.9 (2021-11-26)
|
|
12
|
+
|
|
13
|
+
### Bug fixes
|
|
14
|
+
|
|
15
|
+
Fix an issue where info tooltips would be visible in an inappropriate position when there was no room to place them properly.
|
|
16
|
+
|
|
17
|
+
## 0.19.8 (2021-11-17)
|
|
18
|
+
|
|
19
|
+
### Bug fixes
|
|
20
|
+
|
|
21
|
+
Give the completion tooltip a minimal width, and show ellipsis when completions overflow the tooltip width.
|
|
22
|
+
|
|
23
|
+
### New features
|
|
24
|
+
|
|
25
|
+
`autocompletion` now accepts an `aboveCursor` option to make the completion tooltip show up above the cursor.
|
|
26
|
+
|
|
27
|
+
## 0.19.7 (2021-11-16)
|
|
28
|
+
|
|
29
|
+
### Bug fixes
|
|
30
|
+
|
|
31
|
+
Make option deduplication less aggressive, so that options with different `type` or `apply` fields don't get merged.
|
|
32
|
+
|
|
1
33
|
## 0.19.6 (2021-11-12)
|
|
2
34
|
|
|
3
35
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -246,7 +246,7 @@ class FuzzyMatcher {
|
|
|
246
246
|
let byWordTo = 0, byWordFolded = false;
|
|
247
247
|
// If we've found a partial adjacent match, these track its state
|
|
248
248
|
let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
|
|
249
|
-
let hasLower = /[a-z]/.test(word);
|
|
249
|
+
let hasLower = /[a-z]/.test(word), wordAdjacent = true;
|
|
250
250
|
// Go over the option's text, scanning for the various kinds of matches
|
|
251
251
|
for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* NonWord */; i < e && byWordTo < len;) {
|
|
252
252
|
let next = text.codePointAt(word, i);
|
|
@@ -268,26 +268,30 @@ class FuzzyMatcher {
|
|
|
268
268
|
let ch, type = next < 0xff
|
|
269
269
|
? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Lower */ : next >= 65 && next <= 90 ? 1 /* Upper */ : 0 /* NonWord */)
|
|
270
270
|
: ((ch = text.fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Upper */ : ch != ch.toUpperCase() ? 2 /* Lower */ : 0 /* NonWord */);
|
|
271
|
-
if (
|
|
272
|
-
(chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
|
|
273
|
-
|
|
271
|
+
if (!i || type == 1 /* Upper */ && hasLower || prevType == 0 /* NonWord */ && type != 0 /* NonWord */) {
|
|
272
|
+
if (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
|
|
273
|
+
byWord[byWordTo++] = i;
|
|
274
|
+
else if (byWord.length)
|
|
275
|
+
wordAdjacent = false;
|
|
276
|
+
}
|
|
274
277
|
prevType = type;
|
|
275
278
|
i += text.codePointSize(next);
|
|
276
279
|
}
|
|
277
|
-
if (byWordTo == len && byWord[0] == 0)
|
|
280
|
+
if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
|
|
278
281
|
return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0), byWord, word);
|
|
279
282
|
if (adjacentTo == len && adjacentStart == 0)
|
|
280
|
-
return [-200 /* CaseFold
|
|
283
|
+
return [-200 /* CaseFold */ - word.length, 0, adjacentEnd];
|
|
281
284
|
if (direct > -1)
|
|
282
|
-
return [-700 /* NotStart
|
|
285
|
+
return [-700 /* NotStart */ - word.length, direct, direct + this.pattern.length];
|
|
283
286
|
if (adjacentTo == len)
|
|
284
|
-
return [-200 /* CaseFold */ + -700 /* NotStart
|
|
287
|
+
return [-200 /* CaseFold */ + -700 /* NotStart */ - word.length, adjacentStart, adjacentEnd];
|
|
285
288
|
if (byWordTo == len)
|
|
286
|
-
return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart
|
|
289
|
+
return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */ +
|
|
290
|
+
(wordAdjacent ? 0 : -1100 /* Gap */), byWord, word);
|
|
287
291
|
return chars.length == 2 ? null : this.result((any[0] ? -700 /* NotStart */ : 0) + -200 /* CaseFold */ + -1100 /* Gap */, any, word);
|
|
288
292
|
}
|
|
289
293
|
result(score, positions, word) {
|
|
290
|
-
let result = [score], i = 1;
|
|
294
|
+
let result = [score - word.length], i = 1;
|
|
291
295
|
for (let pos of positions) {
|
|
292
296
|
let to = pos + (this.astral ? text.codePointSize(text.codePointAt(word, pos)) : 1);
|
|
293
297
|
if (i > 1 && result[i - 1] == pos)
|
|
@@ -309,6 +313,7 @@ const completionConfig = state.Facet.define({
|
|
|
309
313
|
maxRenderedOptions: 100,
|
|
310
314
|
defaultKeymap: true,
|
|
311
315
|
optionClass: () => "",
|
|
316
|
+
aboveCursor: false,
|
|
312
317
|
icons: true,
|
|
313
318
|
addToOptions: []
|
|
314
319
|
}, {
|
|
@@ -482,12 +487,14 @@ class CompletionTooltip {
|
|
|
482
487
|
let sel = this.dom.querySelector("[aria-selected]");
|
|
483
488
|
if (!sel || !this.info)
|
|
484
489
|
return null;
|
|
485
|
-
let
|
|
486
|
-
let
|
|
487
|
-
|
|
490
|
+
let listRect = this.dom.getBoundingClientRect();
|
|
491
|
+
let infoRect = this.info.getBoundingClientRect();
|
|
492
|
+
let selRect = sel.getBoundingClientRect();
|
|
493
|
+
if (selRect.top > Math.min(innerHeight, listRect.bottom) - 10 || selRect.bottom < Math.max(0, listRect.top) + 10)
|
|
488
494
|
return null;
|
|
495
|
+
let top = Math.max(0, Math.min(selRect.top, innerHeight - infoRect.height)) - listRect.top;
|
|
489
496
|
let left = this.view.textDirection == view.Direction.RTL;
|
|
490
|
-
let spaceLeft =
|
|
497
|
+
let spaceLeft = listRect.left, spaceRight = innerWidth - listRect.right;
|
|
491
498
|
if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
|
|
492
499
|
left = false;
|
|
493
500
|
else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
|
|
@@ -495,10 +502,12 @@ class CompletionTooltip {
|
|
|
495
502
|
return { top, left };
|
|
496
503
|
}
|
|
497
504
|
positionInfo(pos) {
|
|
498
|
-
if (this.info
|
|
499
|
-
this.info.style.top = pos.top + "px";
|
|
500
|
-
|
|
501
|
-
|
|
505
|
+
if (this.info) {
|
|
506
|
+
this.info.style.top = (pos ? pos.top : -1e6) + "px";
|
|
507
|
+
if (pos) {
|
|
508
|
+
this.info.classList.toggle("cm-completionInfo-left", pos.left);
|
|
509
|
+
this.info.classList.toggle("cm-completionInfo-right", !pos.left);
|
|
510
|
+
}
|
|
502
511
|
}
|
|
503
512
|
}
|
|
504
513
|
createListBox(options, id, range) {
|
|
@@ -570,7 +579,8 @@ function sortOptions(active, state) {
|
|
|
570
579
|
for (let opt of options.sort(cmpOption)) {
|
|
571
580
|
if (result.length == MaxOptions)
|
|
572
581
|
break;
|
|
573
|
-
if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail
|
|
582
|
+
if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail ||
|
|
583
|
+
prev.type != opt.completion.type || prev.apply != opt.completion.apply)
|
|
574
584
|
result.push(opt);
|
|
575
585
|
else if (score(opt.completion) > score(prev))
|
|
576
586
|
result[result.length - 1] = opt;
|
|
@@ -590,7 +600,7 @@ class CompletionDialog {
|
|
|
590
600
|
return selected == this.selected || selected >= this.options.length ? this
|
|
591
601
|
: new CompletionDialog(this.options, makeAttrs(id, selected), this.tooltip, this.timestamp, selected);
|
|
592
602
|
}
|
|
593
|
-
static build(active, state, id, prev) {
|
|
603
|
+
static build(active, state, id, prev, conf) {
|
|
594
604
|
let options = sortOptions(active, state);
|
|
595
605
|
if (!options.length)
|
|
596
606
|
return null;
|
|
@@ -604,7 +614,8 @@ class CompletionDialog {
|
|
|
604
614
|
}
|
|
605
615
|
return new CompletionDialog(options, makeAttrs(id, selected), {
|
|
606
616
|
pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
|
|
607
|
-
create: completionTooltip(completionState)
|
|
617
|
+
create: completionTooltip(completionState),
|
|
618
|
+
above: conf.aboveCursor,
|
|
608
619
|
}, prev ? prev.timestamp : Date.now(), selected);
|
|
609
620
|
}
|
|
610
621
|
map(changes) {
|
|
@@ -632,7 +643,7 @@ class CompletionState {
|
|
|
632
643
|
if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
|
|
633
644
|
active = this.active;
|
|
634
645
|
let open = tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
|
|
635
|
-
!sameResults(active, this.active) ? CompletionDialog.build(active, state, this.id, this.open)
|
|
646
|
+
!sameResults(active, this.active) ? CompletionDialog.build(active, state, this.id, this.open, conf)
|
|
636
647
|
: this.open && tr.docChanged ? this.open.map(tr.changes) : this.open;
|
|
637
648
|
if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
|
|
638
649
|
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
|
|
@@ -968,16 +979,19 @@ const baseTheme = view.EditorView.baseTheme({
|
|
|
968
979
|
"& > ul": {
|
|
969
980
|
fontFamily: "monospace",
|
|
970
981
|
whiteSpace: "nowrap",
|
|
971
|
-
overflow: "auto",
|
|
982
|
+
overflow: "hidden auto",
|
|
972
983
|
maxWidth_fallback: "700px",
|
|
973
984
|
maxWidth: "min(700px, 95vw)",
|
|
985
|
+
minWidth: "250px",
|
|
974
986
|
maxHeight: "10em",
|
|
975
987
|
listStyle: "none",
|
|
976
988
|
margin: 0,
|
|
977
989
|
padding: 0,
|
|
978
990
|
"& > li": {
|
|
991
|
+
overflowX: "hidden",
|
|
992
|
+
textOverflow: "ellipsis",
|
|
979
993
|
cursor: "pointer",
|
|
980
|
-
padding: "1px
|
|
994
|
+
padding: "1px 3px",
|
|
981
995
|
lineHeight: 1.2
|
|
982
996
|
},
|
|
983
997
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,12 @@ interface CompletionConfig {
|
|
|
30
30
|
*/
|
|
31
31
|
defaultKeymap?: boolean;
|
|
32
32
|
/**
|
|
33
|
+
By default, completions are shown below the cursor when there is
|
|
34
|
+
space. Setting this to true will make the extension put the
|
|
35
|
+
completions above the cursor when possible.
|
|
36
|
+
*/
|
|
37
|
+
aboveCursor?: boolean;
|
|
38
|
+
/**
|
|
33
39
|
This can be used to add additional CSS classes to completion
|
|
34
40
|
options.
|
|
35
41
|
*/
|
package/dist/index.js
CHANGED
|
@@ -242,7 +242,7 @@ class FuzzyMatcher {
|
|
|
242
242
|
let byWordTo = 0, byWordFolded = false;
|
|
243
243
|
// If we've found a partial adjacent match, these track its state
|
|
244
244
|
let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
|
|
245
|
-
let hasLower = /[a-z]/.test(word);
|
|
245
|
+
let hasLower = /[a-z]/.test(word), wordAdjacent = true;
|
|
246
246
|
// Go over the option's text, scanning for the various kinds of matches
|
|
247
247
|
for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* NonWord */; i < e && byWordTo < len;) {
|
|
248
248
|
let next = codePointAt(word, i);
|
|
@@ -264,26 +264,30 @@ class FuzzyMatcher {
|
|
|
264
264
|
let ch, type = next < 0xff
|
|
265
265
|
? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Lower */ : next >= 65 && next <= 90 ? 1 /* Upper */ : 0 /* NonWord */)
|
|
266
266
|
: ((ch = fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Upper */ : ch != ch.toUpperCase() ? 2 /* Lower */ : 0 /* NonWord */);
|
|
267
|
-
if (
|
|
268
|
-
(chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
|
|
269
|
-
|
|
267
|
+
if (!i || type == 1 /* Upper */ && hasLower || prevType == 0 /* NonWord */ && type != 0 /* NonWord */) {
|
|
268
|
+
if (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
|
|
269
|
+
byWord[byWordTo++] = i;
|
|
270
|
+
else if (byWord.length)
|
|
271
|
+
wordAdjacent = false;
|
|
272
|
+
}
|
|
270
273
|
prevType = type;
|
|
271
274
|
i += codePointSize(next);
|
|
272
275
|
}
|
|
273
|
-
if (byWordTo == len && byWord[0] == 0)
|
|
276
|
+
if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
|
|
274
277
|
return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0), byWord, word);
|
|
275
278
|
if (adjacentTo == len && adjacentStart == 0)
|
|
276
|
-
return [-200 /* CaseFold
|
|
279
|
+
return [-200 /* CaseFold */ - word.length, 0, adjacentEnd];
|
|
277
280
|
if (direct > -1)
|
|
278
|
-
return [-700 /* NotStart
|
|
281
|
+
return [-700 /* NotStart */ - word.length, direct, direct + this.pattern.length];
|
|
279
282
|
if (adjacentTo == len)
|
|
280
|
-
return [-200 /* CaseFold */ + -700 /* NotStart
|
|
283
|
+
return [-200 /* CaseFold */ + -700 /* NotStart */ - word.length, adjacentStart, adjacentEnd];
|
|
281
284
|
if (byWordTo == len)
|
|
282
|
-
return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart
|
|
285
|
+
return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */ +
|
|
286
|
+
(wordAdjacent ? 0 : -1100 /* Gap */), byWord, word);
|
|
283
287
|
return chars.length == 2 ? null : this.result((any[0] ? -700 /* NotStart */ : 0) + -200 /* CaseFold */ + -1100 /* Gap */, any, word);
|
|
284
288
|
}
|
|
285
289
|
result(score, positions, word) {
|
|
286
|
-
let result = [score], i = 1;
|
|
290
|
+
let result = [score - word.length], i = 1;
|
|
287
291
|
for (let pos of positions) {
|
|
288
292
|
let to = pos + (this.astral ? codePointSize(codePointAt(word, pos)) : 1);
|
|
289
293
|
if (i > 1 && result[i - 1] == pos)
|
|
@@ -305,6 +309,7 @@ const completionConfig = /*@__PURE__*/Facet.define({
|
|
|
305
309
|
maxRenderedOptions: 100,
|
|
306
310
|
defaultKeymap: true,
|
|
307
311
|
optionClass: () => "",
|
|
312
|
+
aboveCursor: false,
|
|
308
313
|
icons: true,
|
|
309
314
|
addToOptions: []
|
|
310
315
|
}, {
|
|
@@ -478,12 +483,14 @@ class CompletionTooltip {
|
|
|
478
483
|
let sel = this.dom.querySelector("[aria-selected]");
|
|
479
484
|
if (!sel || !this.info)
|
|
480
485
|
return null;
|
|
481
|
-
let
|
|
482
|
-
let
|
|
483
|
-
|
|
486
|
+
let listRect = this.dom.getBoundingClientRect();
|
|
487
|
+
let infoRect = this.info.getBoundingClientRect();
|
|
488
|
+
let selRect = sel.getBoundingClientRect();
|
|
489
|
+
if (selRect.top > Math.min(innerHeight, listRect.bottom) - 10 || selRect.bottom < Math.max(0, listRect.top) + 10)
|
|
484
490
|
return null;
|
|
491
|
+
let top = Math.max(0, Math.min(selRect.top, innerHeight - infoRect.height)) - listRect.top;
|
|
485
492
|
let left = this.view.textDirection == Direction.RTL;
|
|
486
|
-
let spaceLeft =
|
|
493
|
+
let spaceLeft = listRect.left, spaceRight = innerWidth - listRect.right;
|
|
487
494
|
if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
|
|
488
495
|
left = false;
|
|
489
496
|
else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
|
|
@@ -491,10 +498,12 @@ class CompletionTooltip {
|
|
|
491
498
|
return { top, left };
|
|
492
499
|
}
|
|
493
500
|
positionInfo(pos) {
|
|
494
|
-
if (this.info
|
|
495
|
-
this.info.style.top = pos.top + "px";
|
|
496
|
-
|
|
497
|
-
|
|
501
|
+
if (this.info) {
|
|
502
|
+
this.info.style.top = (pos ? pos.top : -1e6) + "px";
|
|
503
|
+
if (pos) {
|
|
504
|
+
this.info.classList.toggle("cm-completionInfo-left", pos.left);
|
|
505
|
+
this.info.classList.toggle("cm-completionInfo-right", !pos.left);
|
|
506
|
+
}
|
|
498
507
|
}
|
|
499
508
|
}
|
|
500
509
|
createListBox(options, id, range) {
|
|
@@ -566,7 +575,8 @@ function sortOptions(active, state) {
|
|
|
566
575
|
for (let opt of options.sort(cmpOption)) {
|
|
567
576
|
if (result.length == MaxOptions)
|
|
568
577
|
break;
|
|
569
|
-
if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail
|
|
578
|
+
if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail ||
|
|
579
|
+
prev.type != opt.completion.type || prev.apply != opt.completion.apply)
|
|
570
580
|
result.push(opt);
|
|
571
581
|
else if (score(opt.completion) > score(prev))
|
|
572
582
|
result[result.length - 1] = opt;
|
|
@@ -586,7 +596,7 @@ class CompletionDialog {
|
|
|
586
596
|
return selected == this.selected || selected >= this.options.length ? this
|
|
587
597
|
: new CompletionDialog(this.options, makeAttrs(id, selected), this.tooltip, this.timestamp, selected);
|
|
588
598
|
}
|
|
589
|
-
static build(active, state, id, prev) {
|
|
599
|
+
static build(active, state, id, prev, conf) {
|
|
590
600
|
let options = sortOptions(active, state);
|
|
591
601
|
if (!options.length)
|
|
592
602
|
return null;
|
|
@@ -600,7 +610,8 @@ class CompletionDialog {
|
|
|
600
610
|
}
|
|
601
611
|
return new CompletionDialog(options, makeAttrs(id, selected), {
|
|
602
612
|
pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
|
|
603
|
-
create: completionTooltip(completionState)
|
|
613
|
+
create: completionTooltip(completionState),
|
|
614
|
+
above: conf.aboveCursor,
|
|
604
615
|
}, prev ? prev.timestamp : Date.now(), selected);
|
|
605
616
|
}
|
|
606
617
|
map(changes) {
|
|
@@ -628,7 +639,7 @@ class CompletionState {
|
|
|
628
639
|
if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
|
|
629
640
|
active = this.active;
|
|
630
641
|
let open = tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
|
|
631
|
-
!sameResults(active, this.active) ? CompletionDialog.build(active, state, this.id, this.open)
|
|
642
|
+
!sameResults(active, this.active) ? CompletionDialog.build(active, state, this.id, this.open, conf)
|
|
632
643
|
: this.open && tr.docChanged ? this.open.map(tr.changes) : this.open;
|
|
633
644
|
if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
|
|
634
645
|
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
|
|
@@ -964,16 +975,19 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
|
|
|
964
975
|
"& > ul": {
|
|
965
976
|
fontFamily: "monospace",
|
|
966
977
|
whiteSpace: "nowrap",
|
|
967
|
-
overflow: "auto",
|
|
978
|
+
overflow: "hidden auto",
|
|
968
979
|
maxWidth_fallback: "700px",
|
|
969
980
|
maxWidth: "min(700px, 95vw)",
|
|
981
|
+
minWidth: "250px",
|
|
970
982
|
maxHeight: "10em",
|
|
971
983
|
listStyle: "none",
|
|
972
984
|
margin: 0,
|
|
973
985
|
padding: 0,
|
|
974
986
|
"& > li": {
|
|
987
|
+
overflowX: "hidden",
|
|
988
|
+
textOverflow: "ellipsis",
|
|
975
989
|
cursor: "pointer",
|
|
976
|
-
padding: "1px
|
|
990
|
+
padding: "1px 3px",
|
|
977
991
|
lineHeight: 1.2
|
|
978
992
|
},
|
|
979
993
|
}
|