@codemirror/autocomplete 0.19.2 → 0.19.6
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 +42 -0
- package/dist/index.cjs +218 -158
- package/dist/index.d.ts +40 -3
- package/dist/index.js +219 -161
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,45 @@
|
|
|
1
|
+
## 0.19.6 (2021-11-12)
|
|
2
|
+
|
|
3
|
+
### Bug fixes
|
|
4
|
+
|
|
5
|
+
Fix an issue where parsing a snippet with a field that was labeled only by a number crashed.
|
|
6
|
+
|
|
7
|
+
## 0.19.5 (2021-11-09)
|
|
8
|
+
|
|
9
|
+
### Bug fixes
|
|
10
|
+
|
|
11
|
+
Make sure info tooltips don't stick out of the bottom of the page.
|
|
12
|
+
|
|
13
|
+
### New features
|
|
14
|
+
|
|
15
|
+
The package exports a new function `selectedCompletion`, which can be used to find out which completion is currently selected.
|
|
16
|
+
|
|
17
|
+
Transactions created by picking a completion now have an annotation (`pickedCompletion`) holding the original completion.
|
|
18
|
+
|
|
19
|
+
## 0.19.4 (2021-10-24)
|
|
20
|
+
|
|
21
|
+
### Bug fixes
|
|
22
|
+
|
|
23
|
+
Don't rely on the platform's highlight colors for the active completion, since those are inconsistent and may not be appropriate for the theme.
|
|
24
|
+
|
|
25
|
+
Fix incorrect match underline for some kinds of matched completions.
|
|
26
|
+
|
|
27
|
+
## 0.19.3 (2021-08-31)
|
|
28
|
+
|
|
29
|
+
### Bug fixes
|
|
30
|
+
|
|
31
|
+
Improve the sorting of completions by using `localeCompare`.
|
|
32
|
+
|
|
33
|
+
Fix reading of autocompletions in NVDA screen reader.
|
|
34
|
+
|
|
35
|
+
### New features
|
|
36
|
+
|
|
37
|
+
The new `icons` option can be used to turn off icons in the completion list.
|
|
38
|
+
|
|
39
|
+
The `optionClass` option can now be used to add CSS classes to the options in the completion list.
|
|
40
|
+
|
|
41
|
+
It is now possible to inject additional content into rendered completion options with the `addToOptions` configuration option.
|
|
42
|
+
|
|
1
43
|
## 0.19.2 (2021-08-25)
|
|
2
44
|
|
|
3
45
|
### Bug fixes
|
package/dist/index.cjs
CHANGED
|
@@ -150,6 +150,11 @@ function ensureAnchor(expr, start) {
|
|
|
150
150
|
return expr;
|
|
151
151
|
return new RegExp(`${addStart ? "^" : ""}(?:${source})${addEnd ? "$" : ""}`, (_a = expr.flags) !== null && _a !== void 0 ? _a : (expr.ignoreCase ? "i" : ""));
|
|
152
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
This annotation is added to transactions that are produced by
|
|
155
|
+
picking a completion.
|
|
156
|
+
*/
|
|
157
|
+
const pickedCompletion = state.Annotation.define();
|
|
153
158
|
function applyCompletion(view, option) {
|
|
154
159
|
let apply = option.completion.apply || option.completion.label;
|
|
155
160
|
let result = option.source;
|
|
@@ -157,7 +162,8 @@ function applyCompletion(view, option) {
|
|
|
157
162
|
view.dispatch({
|
|
158
163
|
changes: { from: result.from, to: result.to, insert: apply },
|
|
159
164
|
selection: { anchor: result.from + apply.length },
|
|
160
|
-
userEvent: "input.complete"
|
|
165
|
+
userEvent: "input.complete",
|
|
166
|
+
annotations: pickedCompletion.of(option.completion)
|
|
161
167
|
});
|
|
162
168
|
}
|
|
163
169
|
else {
|
|
@@ -251,7 +257,7 @@ class FuzzyMatcher {
|
|
|
251
257
|
if (next == chars[adjacentTo] || next == folded[adjacentTo]) {
|
|
252
258
|
if (adjacentTo == 0)
|
|
253
259
|
adjacentStart = i;
|
|
254
|
-
adjacentEnd = i;
|
|
260
|
+
adjacentEnd = i + 1;
|
|
255
261
|
adjacentTo++;
|
|
256
262
|
}
|
|
257
263
|
else {
|
|
@@ -301,152 +307,67 @@ const completionConfig = state.Facet.define({
|
|
|
301
307
|
activateOnTyping: true,
|
|
302
308
|
override: null,
|
|
303
309
|
maxRenderedOptions: 100,
|
|
304
|
-
defaultKeymap: true
|
|
310
|
+
defaultKeymap: true,
|
|
311
|
+
optionClass: () => "",
|
|
312
|
+
icons: true,
|
|
313
|
+
addToOptions: []
|
|
305
314
|
}, {
|
|
306
|
-
defaultKeymap: (a, b) => a && b
|
|
315
|
+
defaultKeymap: (a, b) => a && b,
|
|
316
|
+
icons: (a, b) => a && b,
|
|
317
|
+
optionClass: (a, b) => c => joinClass(a(c), b(c)),
|
|
318
|
+
addToOptions: (a, b) => a.concat(b)
|
|
307
319
|
});
|
|
308
320
|
}
|
|
309
321
|
});
|
|
322
|
+
function joinClass(a, b) {
|
|
323
|
+
return a ? b ? a + " " + b : a : b;
|
|
324
|
+
}
|
|
310
325
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
margin: 0,
|
|
323
|
-
padding: 0,
|
|
324
|
-
"& > li": {
|
|
325
|
-
cursor: "pointer",
|
|
326
|
-
padding: "1px 1em 1px 3px",
|
|
327
|
-
lineHeight: 1.2
|
|
326
|
+
function optionContent(config) {
|
|
327
|
+
let content = config.addToOptions.slice();
|
|
328
|
+
if (config.icons)
|
|
329
|
+
content.push({
|
|
330
|
+
render(completion) {
|
|
331
|
+
let icon = document.createElement("div");
|
|
332
|
+
icon.classList.add("cm-completionIcon");
|
|
333
|
+
if (completion.type)
|
|
334
|
+
icon.classList.add(...completion.type.split(/\s+/g).map(cls => "cm-completionIcon-" + cls));
|
|
335
|
+
icon.setAttribute("aria-hidden", "true");
|
|
336
|
+
return icon;
|
|
328
337
|
},
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
338
|
+
position: 20
|
|
339
|
+
});
|
|
340
|
+
content.push({
|
|
341
|
+
render(completion, _s, match) {
|
|
342
|
+
let labelElt = document.createElement("span");
|
|
343
|
+
labelElt.className = "cm-completionLabel";
|
|
344
|
+
let { label } = completion, off = 0;
|
|
345
|
+
for (let j = 1; j < match.length;) {
|
|
346
|
+
let from = match[j++], to = match[j++];
|
|
347
|
+
if (from > off)
|
|
348
|
+
labelElt.appendChild(document.createTextNode(label.slice(off, from)));
|
|
349
|
+
let span = labelElt.appendChild(document.createElement("span"));
|
|
350
|
+
span.appendChild(document.createTextNode(label.slice(from, to)));
|
|
351
|
+
span.className = "cm-completionMatchedText";
|
|
352
|
+
off = to;
|
|
334
353
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
padding: "3px 9px",
|
|
346
|
-
width: "max-content",
|
|
347
|
-
maxWidth: MaxInfoWidth + "px",
|
|
348
|
-
},
|
|
349
|
-
".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
|
|
350
|
-
".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
|
|
351
|
-
"&light .cm-snippetField": { backgroundColor: "#00000022" },
|
|
352
|
-
"&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
|
|
353
|
-
".cm-snippetFieldPosition": {
|
|
354
|
-
verticalAlign: "text-top",
|
|
355
|
-
width: 0,
|
|
356
|
-
height: "1.15em",
|
|
357
|
-
margin: "0 -0.7px -.7em",
|
|
358
|
-
borderLeft: "1.4px dotted #888"
|
|
359
|
-
},
|
|
360
|
-
".cm-completionMatchedText": {
|
|
361
|
-
textDecoration: "underline"
|
|
362
|
-
},
|
|
363
|
-
".cm-completionDetail": {
|
|
364
|
-
marginLeft: "0.5em",
|
|
365
|
-
fontStyle: "italic"
|
|
366
|
-
},
|
|
367
|
-
".cm-completionIcon": {
|
|
368
|
-
fontSize: "90%",
|
|
369
|
-
width: ".8em",
|
|
370
|
-
display: "inline-block",
|
|
371
|
-
textAlign: "center",
|
|
372
|
-
paddingRight: ".6em",
|
|
373
|
-
opacity: "0.6"
|
|
374
|
-
},
|
|
375
|
-
".cm-completionIcon-function, .cm-completionIcon-method": {
|
|
376
|
-
"&:after": { content: "'ƒ'" }
|
|
377
|
-
},
|
|
378
|
-
".cm-completionIcon-class": {
|
|
379
|
-
"&:after": { content: "'○'" }
|
|
380
|
-
},
|
|
381
|
-
".cm-completionIcon-interface": {
|
|
382
|
-
"&:after": { content: "'◌'" }
|
|
383
|
-
},
|
|
384
|
-
".cm-completionIcon-variable": {
|
|
385
|
-
"&:after": { content: "'𝑥'" }
|
|
386
|
-
},
|
|
387
|
-
".cm-completionIcon-constant": {
|
|
388
|
-
"&:after": { content: "'𝐶'" }
|
|
389
|
-
},
|
|
390
|
-
".cm-completionIcon-type": {
|
|
391
|
-
"&:after": { content: "'𝑡'" }
|
|
392
|
-
},
|
|
393
|
-
".cm-completionIcon-enum": {
|
|
394
|
-
"&:after": { content: "'∪'" }
|
|
395
|
-
},
|
|
396
|
-
".cm-completionIcon-property": {
|
|
397
|
-
"&:after": { content: "'□'" }
|
|
398
|
-
},
|
|
399
|
-
".cm-completionIcon-keyword": {
|
|
400
|
-
"&:after": { content: "'🔑\uFE0E'" } // Disable emoji rendering
|
|
401
|
-
},
|
|
402
|
-
".cm-completionIcon-namespace": {
|
|
403
|
-
"&:after": { content: "'▢'" }
|
|
404
|
-
},
|
|
405
|
-
".cm-completionIcon-text": {
|
|
406
|
-
"&:after": { content: "'abc'", fontSize: "50%", verticalAlign: "middle" }
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
function createListBox(options, id, range) {
|
|
411
|
-
const ul = document.createElement("ul");
|
|
412
|
-
ul.id = id;
|
|
413
|
-
ul.setAttribute("role", "listbox");
|
|
414
|
-
ul.setAttribute("aria-expanded", "true");
|
|
415
|
-
for (let i = range.from; i < range.to; i++) {
|
|
416
|
-
let { completion, match } = options[i];
|
|
417
|
-
const li = ul.appendChild(document.createElement("li"));
|
|
418
|
-
li.id = id + "-" + i;
|
|
419
|
-
let icon = li.appendChild(document.createElement("div"));
|
|
420
|
-
icon.classList.add("cm-completionIcon");
|
|
421
|
-
if (completion.type)
|
|
422
|
-
icon.classList.add(...completion.type.split(/\s+/g).map(cls => "cm-completionIcon-" + cls));
|
|
423
|
-
icon.setAttribute("aria-hidden", "true");
|
|
424
|
-
let labelElt = li.appendChild(document.createElement("span"));
|
|
425
|
-
labelElt.className = "cm-completionLabel";
|
|
426
|
-
let { label, detail } = completion, off = 0;
|
|
427
|
-
for (let j = 1; j < match.length;) {
|
|
428
|
-
let from = match[j++], to = match[j++];
|
|
429
|
-
if (from > off)
|
|
430
|
-
labelElt.appendChild(document.createTextNode(label.slice(off, from)));
|
|
431
|
-
let span = labelElt.appendChild(document.createElement("span"));
|
|
432
|
-
span.appendChild(document.createTextNode(label.slice(from, to)));
|
|
433
|
-
span.className = "cm-completionMatchedText";
|
|
434
|
-
off = to;
|
|
435
|
-
}
|
|
436
|
-
if (off < label.length)
|
|
437
|
-
labelElt.appendChild(document.createTextNode(label.slice(off)));
|
|
438
|
-
if (detail) {
|
|
439
|
-
let detailElt = li.appendChild(document.createElement("span"));
|
|
354
|
+
if (off < label.length)
|
|
355
|
+
labelElt.appendChild(document.createTextNode(label.slice(off)));
|
|
356
|
+
return labelElt;
|
|
357
|
+
},
|
|
358
|
+
position: 50
|
|
359
|
+
}, {
|
|
360
|
+
render(completion) {
|
|
361
|
+
if (!completion.detail)
|
|
362
|
+
return null;
|
|
363
|
+
let detailElt = document.createElement("span");
|
|
440
364
|
detailElt.className = "cm-completionDetail";
|
|
441
|
-
detailElt.textContent = detail;
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
if (range.to < options.length)
|
|
448
|
-
ul.classList.add("cm-completionListIncompleteBottom");
|
|
449
|
-
return ul;
|
|
365
|
+
detailElt.textContent = completion.detail;
|
|
366
|
+
return detailElt;
|
|
367
|
+
},
|
|
368
|
+
position: 80
|
|
369
|
+
});
|
|
370
|
+
return content.sort((a, b) => a.position - b.position).map(a => a.render);
|
|
450
371
|
}
|
|
451
372
|
function createInfoDialog(option, view$1) {
|
|
452
373
|
let dom = document.createElement("div");
|
|
@@ -487,6 +408,8 @@ class CompletionTooltip {
|
|
|
487
408
|
let cState = view.state.field(stateField);
|
|
488
409
|
let { options, selected } = cState.open;
|
|
489
410
|
let config = view.state.facet(completionConfig);
|
|
411
|
+
this.optionContent = optionContent(config);
|
|
412
|
+
this.optionClass = config.optionClass;
|
|
490
413
|
this.range = rangeAroundSelected(options.length, selected, config.maxRenderedOptions);
|
|
491
414
|
this.dom = document.createElement("div");
|
|
492
415
|
this.dom.className = "cm-tooltip-autocomplete";
|
|
@@ -499,7 +422,7 @@ class CompletionTooltip {
|
|
|
499
422
|
}
|
|
500
423
|
}
|
|
501
424
|
});
|
|
502
|
-
this.list = this.dom.appendChild(createListBox(options, cState.id, this.range));
|
|
425
|
+
this.list = this.dom.appendChild(this.createListBox(options, cState.id, this.range));
|
|
503
426
|
this.list.addEventListener("scroll", () => {
|
|
504
427
|
if (this.info)
|
|
505
428
|
this.view.requestMeasure(this.placeInfo);
|
|
@@ -519,7 +442,7 @@ class CompletionTooltip {
|
|
|
519
442
|
if (open.selected < this.range.from || open.selected >= this.range.to) {
|
|
520
443
|
this.range = rangeAroundSelected(open.options.length, open.selected, this.view.state.facet(completionConfig).maxRenderedOptions);
|
|
521
444
|
this.list.remove();
|
|
522
|
-
this.list = this.dom.appendChild(createListBox(open.options, cState.id, this.range));
|
|
445
|
+
this.list = this.dom.appendChild(this.createListBox(open.options, cState.id, this.range));
|
|
523
446
|
this.list.addEventListener("scroll", () => {
|
|
524
447
|
if (this.info)
|
|
525
448
|
this.view.requestMeasure(this.placeInfo);
|
|
@@ -557,17 +480,17 @@ class CompletionTooltip {
|
|
|
557
480
|
}
|
|
558
481
|
measureInfo() {
|
|
559
482
|
let sel = this.dom.querySelector("[aria-selected]");
|
|
560
|
-
if (!sel)
|
|
483
|
+
if (!sel || !this.info)
|
|
561
484
|
return null;
|
|
562
|
-
let rect = this.dom.getBoundingClientRect();
|
|
563
|
-
let top = sel.getBoundingClientRect().top - rect.top;
|
|
485
|
+
let rect = this.dom.getBoundingClientRect(), infoRect = this.info.getBoundingClientRect();
|
|
486
|
+
let top = Math.min(sel.getBoundingClientRect().top, innerHeight - infoRect.height) - rect.top;
|
|
564
487
|
if (top < 0 || top > this.list.clientHeight - 10)
|
|
565
488
|
return null;
|
|
566
489
|
let left = this.view.textDirection == view.Direction.RTL;
|
|
567
490
|
let spaceLeft = rect.left, spaceRight = innerWidth - rect.right;
|
|
568
|
-
if (left && spaceLeft < Math.min(
|
|
491
|
+
if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
|
|
569
492
|
left = false;
|
|
570
|
-
else if (!left && spaceRight < Math.min(
|
|
493
|
+
else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
|
|
571
494
|
left = true;
|
|
572
495
|
return { top, left };
|
|
573
496
|
}
|
|
@@ -578,6 +501,30 @@ class CompletionTooltip {
|
|
|
578
501
|
this.info.classList.toggle("cm-completionInfo-right", !pos.left);
|
|
579
502
|
}
|
|
580
503
|
}
|
|
504
|
+
createListBox(options, id, range) {
|
|
505
|
+
const ul = document.createElement("ul");
|
|
506
|
+
ul.id = id;
|
|
507
|
+
ul.setAttribute("role", "listbox");
|
|
508
|
+
for (let i = range.from; i < range.to; i++) {
|
|
509
|
+
let { completion, match } = options[i];
|
|
510
|
+
const li = ul.appendChild(document.createElement("li"));
|
|
511
|
+
li.id = id + "-" + i;
|
|
512
|
+
li.setAttribute("role", "option");
|
|
513
|
+
let cls = this.optionClass(completion);
|
|
514
|
+
if (cls)
|
|
515
|
+
li.className = cls;
|
|
516
|
+
for (let source of this.optionContent) {
|
|
517
|
+
let node = source(completion, this.view.state, match);
|
|
518
|
+
if (node)
|
|
519
|
+
li.appendChild(node);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
if (range.from)
|
|
523
|
+
ul.classList.add("cm-completionListIncompleteTop");
|
|
524
|
+
if (range.to < options.length)
|
|
525
|
+
ul.classList.add("cm-completionListIncompleteBottom");
|
|
526
|
+
return ul;
|
|
527
|
+
}
|
|
581
528
|
}
|
|
582
529
|
// We allocate a new function instance every time the completion
|
|
583
530
|
// changes to force redrawing/repositioning of the tooltip
|
|
@@ -712,20 +659,24 @@ function sameResults(a, b) {
|
|
|
712
659
|
return false;
|
|
713
660
|
}
|
|
714
661
|
}
|
|
662
|
+
const baseAttrs = {
|
|
663
|
+
"aria-autocomplete": "list",
|
|
664
|
+
"aria-expanded": "false"
|
|
665
|
+
};
|
|
715
666
|
function makeAttrs(id, selected) {
|
|
716
667
|
return {
|
|
717
668
|
"aria-autocomplete": "list",
|
|
669
|
+
"aria-expanded": "true",
|
|
718
670
|
"aria-activedescendant": id + "-" + selected,
|
|
719
|
-
"aria-
|
|
671
|
+
"aria-controls": id
|
|
720
672
|
};
|
|
721
673
|
}
|
|
722
|
-
const
|
|
674
|
+
const none = [];
|
|
723
675
|
function cmpOption(a, b) {
|
|
724
676
|
let dScore = b.match[0] - a.match[0];
|
|
725
677
|
if (dScore)
|
|
726
678
|
return dScore;
|
|
727
|
-
|
|
728
|
-
return lA < lB ? -1 : lA == lB ? 0 : 1;
|
|
679
|
+
return a.completion.label.localeCompare(b.completion.label);
|
|
729
680
|
}
|
|
730
681
|
function getUserEvent(tr) {
|
|
731
682
|
return tr.isUserEvent("input.type") ? "input" : tr.isUserEvent("delete.backward") ? "delete" : null;
|
|
@@ -836,7 +787,7 @@ Accept the current completion.
|
|
|
836
787
|
*/
|
|
837
788
|
const acceptCompletion = (view) => {
|
|
838
789
|
let cState = view.state.field(completionState, false);
|
|
839
|
-
if (!cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin)
|
|
790
|
+
if (view.state.readOnly || !cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin)
|
|
840
791
|
return false;
|
|
841
792
|
applyCompletion(view, cState.open.options[cState.open.selected]);
|
|
842
793
|
return true;
|
|
@@ -1012,6 +963,106 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
|
|
|
1012
963
|
}
|
|
1013
964
|
});
|
|
1014
965
|
|
|
966
|
+
const baseTheme = view.EditorView.baseTheme({
|
|
967
|
+
".cm-tooltip.cm-tooltip-autocomplete": {
|
|
968
|
+
"& > ul": {
|
|
969
|
+
fontFamily: "monospace",
|
|
970
|
+
whiteSpace: "nowrap",
|
|
971
|
+
overflow: "auto",
|
|
972
|
+
maxWidth_fallback: "700px",
|
|
973
|
+
maxWidth: "min(700px, 95vw)",
|
|
974
|
+
maxHeight: "10em",
|
|
975
|
+
listStyle: "none",
|
|
976
|
+
margin: 0,
|
|
977
|
+
padding: 0,
|
|
978
|
+
"& > li": {
|
|
979
|
+
cursor: "pointer",
|
|
980
|
+
padding: "1px 1em 1px 3px",
|
|
981
|
+
lineHeight: 1.2
|
|
982
|
+
},
|
|
983
|
+
}
|
|
984
|
+
},
|
|
985
|
+
"&light .cm-tooltip-autocomplete ul li[aria-selected]": {
|
|
986
|
+
background: "#39e",
|
|
987
|
+
color: "white",
|
|
988
|
+
},
|
|
989
|
+
"&dark .cm-tooltip-autocomplete ul li[aria-selected]": {
|
|
990
|
+
background: "#347",
|
|
991
|
+
color: "white",
|
|
992
|
+
},
|
|
993
|
+
".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after": {
|
|
994
|
+
content: '"···"',
|
|
995
|
+
opacity: 0.5,
|
|
996
|
+
display: "block",
|
|
997
|
+
textAlign: "center"
|
|
998
|
+
},
|
|
999
|
+
".cm-tooltip.cm-completionInfo": {
|
|
1000
|
+
position: "absolute",
|
|
1001
|
+
padding: "3px 9px",
|
|
1002
|
+
width: "max-content",
|
|
1003
|
+
maxWidth: "300px",
|
|
1004
|
+
},
|
|
1005
|
+
".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
|
|
1006
|
+
".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
|
|
1007
|
+
"&light .cm-snippetField": { backgroundColor: "#00000022" },
|
|
1008
|
+
"&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
|
|
1009
|
+
".cm-snippetFieldPosition": {
|
|
1010
|
+
verticalAlign: "text-top",
|
|
1011
|
+
width: 0,
|
|
1012
|
+
height: "1.15em",
|
|
1013
|
+
margin: "0 -0.7px -.7em",
|
|
1014
|
+
borderLeft: "1.4px dotted #888"
|
|
1015
|
+
},
|
|
1016
|
+
".cm-completionMatchedText": {
|
|
1017
|
+
textDecoration: "underline"
|
|
1018
|
+
},
|
|
1019
|
+
".cm-completionDetail": {
|
|
1020
|
+
marginLeft: "0.5em",
|
|
1021
|
+
fontStyle: "italic"
|
|
1022
|
+
},
|
|
1023
|
+
".cm-completionIcon": {
|
|
1024
|
+
fontSize: "90%",
|
|
1025
|
+
width: ".8em",
|
|
1026
|
+
display: "inline-block",
|
|
1027
|
+
textAlign: "center",
|
|
1028
|
+
paddingRight: ".6em",
|
|
1029
|
+
opacity: "0.6"
|
|
1030
|
+
},
|
|
1031
|
+
".cm-completionIcon-function, .cm-completionIcon-method": {
|
|
1032
|
+
"&:after": { content: "'ƒ'" }
|
|
1033
|
+
},
|
|
1034
|
+
".cm-completionIcon-class": {
|
|
1035
|
+
"&:after": { content: "'○'" }
|
|
1036
|
+
},
|
|
1037
|
+
".cm-completionIcon-interface": {
|
|
1038
|
+
"&:after": { content: "'◌'" }
|
|
1039
|
+
},
|
|
1040
|
+
".cm-completionIcon-variable": {
|
|
1041
|
+
"&:after": { content: "'𝑥'" }
|
|
1042
|
+
},
|
|
1043
|
+
".cm-completionIcon-constant": {
|
|
1044
|
+
"&:after": { content: "'𝐶'" }
|
|
1045
|
+
},
|
|
1046
|
+
".cm-completionIcon-type": {
|
|
1047
|
+
"&:after": { content: "'𝑡'" }
|
|
1048
|
+
},
|
|
1049
|
+
".cm-completionIcon-enum": {
|
|
1050
|
+
"&:after": { content: "'∪'" }
|
|
1051
|
+
},
|
|
1052
|
+
".cm-completionIcon-property": {
|
|
1053
|
+
"&:after": { content: "'□'" }
|
|
1054
|
+
},
|
|
1055
|
+
".cm-completionIcon-keyword": {
|
|
1056
|
+
"&:after": { content: "'🔑\uFE0E'" } // Disable emoji rendering
|
|
1057
|
+
},
|
|
1058
|
+
".cm-completionIcon-namespace": {
|
|
1059
|
+
"&:after": { content: "'▢'" }
|
|
1060
|
+
},
|
|
1061
|
+
".cm-completionIcon-text": {
|
|
1062
|
+
"&:after": { content: "'abc'", fontSize: "50%", verticalAlign: "middle" }
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1015
1066
|
class FieldPos {
|
|
1016
1067
|
constructor(field, line, from, to) {
|
|
1017
1068
|
this.field = field;
|
|
@@ -1057,7 +1108,7 @@ class Snippet {
|
|
|
1057
1108
|
let lines = [], positions = [], m;
|
|
1058
1109
|
for (let line of template.split(/\r\n?|\n/)) {
|
|
1059
1110
|
while (m = /[#$]\{(?:(\d+)(?::([^}]*))?|([^}]*))\}/.exec(line)) {
|
|
1060
|
-
let seq = m[1] ? +m[1] : null, name = m[2] || m[3], found = -1;
|
|
1111
|
+
let seq = m[1] ? +m[1] : null, name = m[2] || m[3] || "", found = -1;
|
|
1061
1112
|
for (let i = 0; i < fields.length; i++) {
|
|
1062
1113
|
if (seq != null ? fields[i].seq == seq : name ? fields[i].name == name : false)
|
|
1063
1114
|
found = i;
|
|
@@ -1066,7 +1117,7 @@ class Snippet {
|
|
|
1066
1117
|
let i = 0;
|
|
1067
1118
|
while (i < fields.length && (seq == null || (fields[i].seq != null && fields[i].seq < seq)))
|
|
1068
1119
|
i++;
|
|
1069
|
-
fields.splice(i, 0, { seq, name
|
|
1120
|
+
fields.splice(i, 0, { seq, name });
|
|
1070
1121
|
found = i;
|
|
1071
1122
|
for (let pos of positions)
|
|
1072
1123
|
if (pos.field >= found)
|
|
@@ -1162,8 +1213,7 @@ function snippet(template) {
|
|
|
1162
1213
|
let active = new ActiveSnippet(ranges, 0);
|
|
1163
1214
|
let effects = spec.effects = [setActive.of(active)];
|
|
1164
1215
|
if (editor.state.field(snippetState, false) === undefined)
|
|
1165
|
-
effects.push(state.StateEffect.appendConfig.of([snippetState
|
|
1166
|
-
snippetPointerHandler, baseTheme]));
|
|
1216
|
+
effects.push(state.StateEffect.appendConfig.of([snippetState, addSnippetKeymap, snippetPointerHandler, baseTheme]));
|
|
1167
1217
|
}
|
|
1168
1218
|
editor.dispatch(editor.state.update(spec));
|
|
1169
1219
|
};
|
|
@@ -1213,7 +1263,7 @@ to [`clearSnippet`](https://codemirror.net/6/docs/ref/#autocomplete.clearSnippet
|
|
|
1213
1263
|
const snippetKeymap = state.Facet.define({
|
|
1214
1264
|
combine(maps) { return maps.length ? maps[0] : defaultSnippetKeymap; }
|
|
1215
1265
|
});
|
|
1216
|
-
const addSnippetKeymap = state.Prec.
|
|
1266
|
+
const addSnippetKeymap = state.Prec.highest(view.keymap.compute([snippetKeymap], state => state.facet(snippetKeymap)));
|
|
1217
1267
|
/**
|
|
1218
1268
|
Create a completion from a snippet. Returns an object with the
|
|
1219
1269
|
properties from `completion`, plus an `apply` function that
|
|
@@ -1347,7 +1397,7 @@ const completionKeymap = [
|
|
|
1347
1397
|
{ key: "PageUp", run: moveCompletionSelection(false, "page") },
|
|
1348
1398
|
{ key: "Enter", run: acceptCompletion }
|
|
1349
1399
|
];
|
|
1350
|
-
const completionKeymapExt = state.Prec.
|
|
1400
|
+
const completionKeymapExt = state.Prec.highest(view.keymap.computeN([completionConfig], state => state.facet(completionConfig).defaultKeymap ? [completionKeymap] : []));
|
|
1351
1401
|
/**
|
|
1352
1402
|
Get the current completion status. When completions are available,
|
|
1353
1403
|
this will return `"active"`. When completions are pending (in the
|
|
@@ -1367,6 +1417,14 @@ function currentCompletions(state) {
|
|
|
1367
1417
|
let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
|
|
1368
1418
|
return open ? open.options.map(o => o.completion) : [];
|
|
1369
1419
|
}
|
|
1420
|
+
/**
|
|
1421
|
+
Return the currently selected completion, if any.
|
|
1422
|
+
*/
|
|
1423
|
+
function selectedCompletion(state) {
|
|
1424
|
+
var _a;
|
|
1425
|
+
let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
|
|
1426
|
+
return open ? open.options[open.selected].completion : null;
|
|
1427
|
+
}
|
|
1370
1428
|
|
|
1371
1429
|
exports.CompletionContext = CompletionContext;
|
|
1372
1430
|
exports.acceptCompletion = acceptCompletion;
|
|
@@ -1382,7 +1440,9 @@ exports.ifIn = ifIn;
|
|
|
1382
1440
|
exports.ifNotIn = ifNotIn;
|
|
1383
1441
|
exports.moveCompletionSelection = moveCompletionSelection;
|
|
1384
1442
|
exports.nextSnippetField = nextSnippetField;
|
|
1443
|
+
exports.pickedCompletion = pickedCompletion;
|
|
1385
1444
|
exports.prevSnippetField = prevSnippetField;
|
|
1445
|
+
exports.selectedCompletion = selectedCompletion;
|
|
1386
1446
|
exports.snippet = snippet;
|
|
1387
1447
|
exports.snippetCompletion = snippetCompletion;
|
|
1388
1448
|
exports.snippetKeymap = snippetKeymap;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as _codemirror_state from '@codemirror/state';
|
|
1
2
|
import { EditorState, Transaction, StateCommand, Facet, Extension } from '@codemirror/state';
|
|
2
3
|
import { EditorView, KeyBinding, Command } from '@codemirror/view';
|
|
3
4
|
import * as _lezer_common from '@lezer/common';
|
|
@@ -12,7 +13,8 @@ interface CompletionConfig {
|
|
|
12
13
|
Override the completion sources used. By default, they will be
|
|
13
14
|
taken from the `"autocomplete"` [language
|
|
14
15
|
data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) (which should hold
|
|
15
|
-
[completion sources](https://codemirror.net/6/docs/ref/#autocomplete.CompletionSource)
|
|
16
|
+
[completion sources](https://codemirror.net/6/docs/ref/#autocomplete.CompletionSource) or arrays
|
|
17
|
+
of [completions](https://codemirror.net/6/docs/ref/#autocomplete.Completion)).
|
|
16
18
|
*/
|
|
17
19
|
override?: readonly CompletionSource[] | null;
|
|
18
20
|
/**
|
|
@@ -27,6 +29,30 @@ interface CompletionConfig {
|
|
|
27
29
|
same keys.)
|
|
28
30
|
*/
|
|
29
31
|
defaultKeymap?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
This can be used to add additional CSS classes to completion
|
|
34
|
+
options.
|
|
35
|
+
*/
|
|
36
|
+
optionClass?: (completion: Completion) => string;
|
|
37
|
+
/**
|
|
38
|
+
By default, the library will render icons based on the
|
|
39
|
+
completion's [type](https://codemirror.net/6/docs/ref/#autocomplete.Completion.type) in front of
|
|
40
|
+
each option. Set this to false to turn that off.
|
|
41
|
+
*/
|
|
42
|
+
icons?: boolean;
|
|
43
|
+
/**
|
|
44
|
+
This option can be used to inject additional content into
|
|
45
|
+
options. The `render` function will be called for each visible
|
|
46
|
+
completion, and should produce a DOM node to show. `position`
|
|
47
|
+
determines where in the DOM the result appears, relative to
|
|
48
|
+
other added widgets and the standard content. The default icons
|
|
49
|
+
have position 20, the label position 50, and the detail position
|
|
50
|
+
70.
|
|
51
|
+
*/
|
|
52
|
+
addToOptions?: {
|
|
53
|
+
render: (completion: Completion, state: EditorState) => Node | null;
|
|
54
|
+
position: number;
|
|
55
|
+
}[];
|
|
30
56
|
}
|
|
31
57
|
|
|
32
58
|
/**
|
|
@@ -55,7 +81,9 @@ interface Completion {
|
|
|
55
81
|
its [label](https://codemirror.net/6/docs/ref/#autocomplete.Completion.label). When this holds a
|
|
56
82
|
string, the completion range is replaced by that string. When it
|
|
57
83
|
is a function, that function is called to perform the
|
|
58
|
-
completion.
|
|
84
|
+
completion. If it fires a transaction, it is responsible for
|
|
85
|
+
adding the [`pickedCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.pickedCompletion)
|
|
86
|
+
annotation to it.
|
|
59
87
|
*/
|
|
60
88
|
apply?: string | ((view: EditorView, completion: Completion, from: number, to: number) => void);
|
|
61
89
|
/**
|
|
@@ -210,6 +238,11 @@ interface CompletionResult {
|
|
|
210
238
|
*/
|
|
211
239
|
filter?: boolean;
|
|
212
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
This annotation is added to transactions that are produced by
|
|
243
|
+
picking a completion.
|
|
244
|
+
*/
|
|
245
|
+
declare const pickedCompletion: _codemirror_state.AnnotationType<Completion>;
|
|
213
246
|
|
|
214
247
|
/**
|
|
215
248
|
Convert a snippet template to a function that can apply it.
|
|
@@ -319,5 +352,9 @@ declare function completionStatus(state: EditorState): null | "active" | "pendin
|
|
|
319
352
|
Returns the available completions as an array.
|
|
320
353
|
*/
|
|
321
354
|
declare function currentCompletions(state: EditorState): readonly Completion[];
|
|
355
|
+
/**
|
|
356
|
+
Return the currently selected completion, if any.
|
|
357
|
+
*/
|
|
358
|
+
declare function selectedCompletion(state: EditorState): Completion | null;
|
|
322
359
|
|
|
323
|
-
export { Completion, CompletionContext, CompletionResult, CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifIn, ifNotIn, moveCompletionSelection, nextSnippetField, prevSnippetField, snippet, snippetCompletion, snippetKeymap, startCompletion };
|
|
360
|
+
export { Completion, CompletionContext, CompletionResult, CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifIn, ifNotIn, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Facet, combineConfig, StateEffect, StateField,
|
|
2
|
-
import {
|
|
1
|
+
import { Annotation, Facet, combineConfig, StateEffect, StateField, Prec, EditorSelection, Text } from '@codemirror/state';
|
|
2
|
+
import { Direction, logException, EditorView, ViewPlugin, Decoration, WidgetType, keymap } from '@codemirror/view';
|
|
3
3
|
import { showTooltip } from '@codemirror/tooltip';
|
|
4
4
|
import { syntaxTree, indentUnit } from '@codemirror/language';
|
|
5
5
|
import { codePointAt, codePointSize, fromCodePoint } from '@codemirror/text';
|
|
@@ -146,6 +146,11 @@ function ensureAnchor(expr, start) {
|
|
|
146
146
|
return expr;
|
|
147
147
|
return new RegExp(`${addStart ? "^" : ""}(?:${source})${addEnd ? "$" : ""}`, (_a = expr.flags) !== null && _a !== void 0 ? _a : (expr.ignoreCase ? "i" : ""));
|
|
148
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
This annotation is added to transactions that are produced by
|
|
151
|
+
picking a completion.
|
|
152
|
+
*/
|
|
153
|
+
const pickedCompletion = /*@__PURE__*/Annotation.define();
|
|
149
154
|
function applyCompletion(view, option) {
|
|
150
155
|
let apply = option.completion.apply || option.completion.label;
|
|
151
156
|
let result = option.source;
|
|
@@ -153,7 +158,8 @@ function applyCompletion(view, option) {
|
|
|
153
158
|
view.dispatch({
|
|
154
159
|
changes: { from: result.from, to: result.to, insert: apply },
|
|
155
160
|
selection: { anchor: result.from + apply.length },
|
|
156
|
-
userEvent: "input.complete"
|
|
161
|
+
userEvent: "input.complete",
|
|
162
|
+
annotations: pickedCompletion.of(option.completion)
|
|
157
163
|
});
|
|
158
164
|
}
|
|
159
165
|
else {
|
|
@@ -247,7 +253,7 @@ class FuzzyMatcher {
|
|
|
247
253
|
if (next == chars[adjacentTo] || next == folded[adjacentTo]) {
|
|
248
254
|
if (adjacentTo == 0)
|
|
249
255
|
adjacentStart = i;
|
|
250
|
-
adjacentEnd = i;
|
|
256
|
+
adjacentEnd = i + 1;
|
|
251
257
|
adjacentTo++;
|
|
252
258
|
}
|
|
253
259
|
else {
|
|
@@ -297,152 +303,67 @@ const completionConfig = /*@__PURE__*/Facet.define({
|
|
|
297
303
|
activateOnTyping: true,
|
|
298
304
|
override: null,
|
|
299
305
|
maxRenderedOptions: 100,
|
|
300
|
-
defaultKeymap: true
|
|
306
|
+
defaultKeymap: true,
|
|
307
|
+
optionClass: () => "",
|
|
308
|
+
icons: true,
|
|
309
|
+
addToOptions: []
|
|
301
310
|
}, {
|
|
302
|
-
defaultKeymap: (a, b) => a && b
|
|
311
|
+
defaultKeymap: (a, b) => a && b,
|
|
312
|
+
icons: (a, b) => a && b,
|
|
313
|
+
optionClass: (a, b) => c => joinClass(a(c), b(c)),
|
|
314
|
+
addToOptions: (a, b) => a.concat(b)
|
|
303
315
|
});
|
|
304
316
|
}
|
|
305
317
|
});
|
|
318
|
+
function joinClass(a, b) {
|
|
319
|
+
return a ? b ? a + " " + b : a : b;
|
|
320
|
+
}
|
|
306
321
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
margin: 0,
|
|
319
|
-
padding: 0,
|
|
320
|
-
"& > li": {
|
|
321
|
-
cursor: "pointer",
|
|
322
|
-
padding: "1px 1em 1px 3px",
|
|
323
|
-
lineHeight: 1.2
|
|
322
|
+
function optionContent(config) {
|
|
323
|
+
let content = config.addToOptions.slice();
|
|
324
|
+
if (config.icons)
|
|
325
|
+
content.push({
|
|
326
|
+
render(completion) {
|
|
327
|
+
let icon = document.createElement("div");
|
|
328
|
+
icon.classList.add("cm-completionIcon");
|
|
329
|
+
if (completion.type)
|
|
330
|
+
icon.classList.add(...completion.type.split(/\s+/g).map(cls => "cm-completionIcon-" + cls));
|
|
331
|
+
icon.setAttribute("aria-hidden", "true");
|
|
332
|
+
return icon;
|
|
324
333
|
},
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
334
|
+
position: 20
|
|
335
|
+
});
|
|
336
|
+
content.push({
|
|
337
|
+
render(completion, _s, match) {
|
|
338
|
+
let labelElt = document.createElement("span");
|
|
339
|
+
labelElt.className = "cm-completionLabel";
|
|
340
|
+
let { label } = completion, off = 0;
|
|
341
|
+
for (let j = 1; j < match.length;) {
|
|
342
|
+
let from = match[j++], to = match[j++];
|
|
343
|
+
if (from > off)
|
|
344
|
+
labelElt.appendChild(document.createTextNode(label.slice(off, from)));
|
|
345
|
+
let span = labelElt.appendChild(document.createElement("span"));
|
|
346
|
+
span.appendChild(document.createTextNode(label.slice(from, to)));
|
|
347
|
+
span.className = "cm-completionMatchedText";
|
|
348
|
+
off = to;
|
|
330
349
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
padding: "3px 9px",
|
|
342
|
-
width: "max-content",
|
|
343
|
-
maxWidth: MaxInfoWidth + "px",
|
|
344
|
-
},
|
|
345
|
-
".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
|
|
346
|
-
".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
|
|
347
|
-
"&light .cm-snippetField": { backgroundColor: "#00000022" },
|
|
348
|
-
"&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
|
|
349
|
-
".cm-snippetFieldPosition": {
|
|
350
|
-
verticalAlign: "text-top",
|
|
351
|
-
width: 0,
|
|
352
|
-
height: "1.15em",
|
|
353
|
-
margin: "0 -0.7px -.7em",
|
|
354
|
-
borderLeft: "1.4px dotted #888"
|
|
355
|
-
},
|
|
356
|
-
".cm-completionMatchedText": {
|
|
357
|
-
textDecoration: "underline"
|
|
358
|
-
},
|
|
359
|
-
".cm-completionDetail": {
|
|
360
|
-
marginLeft: "0.5em",
|
|
361
|
-
fontStyle: "italic"
|
|
362
|
-
},
|
|
363
|
-
".cm-completionIcon": {
|
|
364
|
-
fontSize: "90%",
|
|
365
|
-
width: ".8em",
|
|
366
|
-
display: "inline-block",
|
|
367
|
-
textAlign: "center",
|
|
368
|
-
paddingRight: ".6em",
|
|
369
|
-
opacity: "0.6"
|
|
370
|
-
},
|
|
371
|
-
".cm-completionIcon-function, .cm-completionIcon-method": {
|
|
372
|
-
"&:after": { content: "'ƒ'" }
|
|
373
|
-
},
|
|
374
|
-
".cm-completionIcon-class": {
|
|
375
|
-
"&:after": { content: "'○'" }
|
|
376
|
-
},
|
|
377
|
-
".cm-completionIcon-interface": {
|
|
378
|
-
"&:after": { content: "'◌'" }
|
|
379
|
-
},
|
|
380
|
-
".cm-completionIcon-variable": {
|
|
381
|
-
"&:after": { content: "'𝑥'" }
|
|
382
|
-
},
|
|
383
|
-
".cm-completionIcon-constant": {
|
|
384
|
-
"&:after": { content: "'𝐶'" }
|
|
385
|
-
},
|
|
386
|
-
".cm-completionIcon-type": {
|
|
387
|
-
"&:after": { content: "'𝑡'" }
|
|
388
|
-
},
|
|
389
|
-
".cm-completionIcon-enum": {
|
|
390
|
-
"&:after": { content: "'∪'" }
|
|
391
|
-
},
|
|
392
|
-
".cm-completionIcon-property": {
|
|
393
|
-
"&:after": { content: "'□'" }
|
|
394
|
-
},
|
|
395
|
-
".cm-completionIcon-keyword": {
|
|
396
|
-
"&:after": { content: "'🔑\uFE0E'" } // Disable emoji rendering
|
|
397
|
-
},
|
|
398
|
-
".cm-completionIcon-namespace": {
|
|
399
|
-
"&:after": { content: "'▢'" }
|
|
400
|
-
},
|
|
401
|
-
".cm-completionIcon-text": {
|
|
402
|
-
"&:after": { content: "'abc'", fontSize: "50%", verticalAlign: "middle" }
|
|
403
|
-
}
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
function createListBox(options, id, range) {
|
|
407
|
-
const ul = document.createElement("ul");
|
|
408
|
-
ul.id = id;
|
|
409
|
-
ul.setAttribute("role", "listbox");
|
|
410
|
-
ul.setAttribute("aria-expanded", "true");
|
|
411
|
-
for (let i = range.from; i < range.to; i++) {
|
|
412
|
-
let { completion, match } = options[i];
|
|
413
|
-
const li = ul.appendChild(document.createElement("li"));
|
|
414
|
-
li.id = id + "-" + i;
|
|
415
|
-
let icon = li.appendChild(document.createElement("div"));
|
|
416
|
-
icon.classList.add("cm-completionIcon");
|
|
417
|
-
if (completion.type)
|
|
418
|
-
icon.classList.add(...completion.type.split(/\s+/g).map(cls => "cm-completionIcon-" + cls));
|
|
419
|
-
icon.setAttribute("aria-hidden", "true");
|
|
420
|
-
let labelElt = li.appendChild(document.createElement("span"));
|
|
421
|
-
labelElt.className = "cm-completionLabel";
|
|
422
|
-
let { label, detail } = completion, off = 0;
|
|
423
|
-
for (let j = 1; j < match.length;) {
|
|
424
|
-
let from = match[j++], to = match[j++];
|
|
425
|
-
if (from > off)
|
|
426
|
-
labelElt.appendChild(document.createTextNode(label.slice(off, from)));
|
|
427
|
-
let span = labelElt.appendChild(document.createElement("span"));
|
|
428
|
-
span.appendChild(document.createTextNode(label.slice(from, to)));
|
|
429
|
-
span.className = "cm-completionMatchedText";
|
|
430
|
-
off = to;
|
|
431
|
-
}
|
|
432
|
-
if (off < label.length)
|
|
433
|
-
labelElt.appendChild(document.createTextNode(label.slice(off)));
|
|
434
|
-
if (detail) {
|
|
435
|
-
let detailElt = li.appendChild(document.createElement("span"));
|
|
350
|
+
if (off < label.length)
|
|
351
|
+
labelElt.appendChild(document.createTextNode(label.slice(off)));
|
|
352
|
+
return labelElt;
|
|
353
|
+
},
|
|
354
|
+
position: 50
|
|
355
|
+
}, {
|
|
356
|
+
render(completion) {
|
|
357
|
+
if (!completion.detail)
|
|
358
|
+
return null;
|
|
359
|
+
let detailElt = document.createElement("span");
|
|
436
360
|
detailElt.className = "cm-completionDetail";
|
|
437
|
-
detailElt.textContent = detail;
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
if (range.to < options.length)
|
|
444
|
-
ul.classList.add("cm-completionListIncompleteBottom");
|
|
445
|
-
return ul;
|
|
361
|
+
detailElt.textContent = completion.detail;
|
|
362
|
+
return detailElt;
|
|
363
|
+
},
|
|
364
|
+
position: 80
|
|
365
|
+
});
|
|
366
|
+
return content.sort((a, b) => a.position - b.position).map(a => a.render);
|
|
446
367
|
}
|
|
447
368
|
function createInfoDialog(option, view) {
|
|
448
369
|
let dom = document.createElement("div");
|
|
@@ -483,6 +404,8 @@ class CompletionTooltip {
|
|
|
483
404
|
let cState = view.state.field(stateField);
|
|
484
405
|
let { options, selected } = cState.open;
|
|
485
406
|
let config = view.state.facet(completionConfig);
|
|
407
|
+
this.optionContent = optionContent(config);
|
|
408
|
+
this.optionClass = config.optionClass;
|
|
486
409
|
this.range = rangeAroundSelected(options.length, selected, config.maxRenderedOptions);
|
|
487
410
|
this.dom = document.createElement("div");
|
|
488
411
|
this.dom.className = "cm-tooltip-autocomplete";
|
|
@@ -495,7 +418,7 @@ class CompletionTooltip {
|
|
|
495
418
|
}
|
|
496
419
|
}
|
|
497
420
|
});
|
|
498
|
-
this.list = this.dom.appendChild(createListBox(options, cState.id, this.range));
|
|
421
|
+
this.list = this.dom.appendChild(this.createListBox(options, cState.id, this.range));
|
|
499
422
|
this.list.addEventListener("scroll", () => {
|
|
500
423
|
if (this.info)
|
|
501
424
|
this.view.requestMeasure(this.placeInfo);
|
|
@@ -515,7 +438,7 @@ class CompletionTooltip {
|
|
|
515
438
|
if (open.selected < this.range.from || open.selected >= this.range.to) {
|
|
516
439
|
this.range = rangeAroundSelected(open.options.length, open.selected, this.view.state.facet(completionConfig).maxRenderedOptions);
|
|
517
440
|
this.list.remove();
|
|
518
|
-
this.list = this.dom.appendChild(createListBox(open.options, cState.id, this.range));
|
|
441
|
+
this.list = this.dom.appendChild(this.createListBox(open.options, cState.id, this.range));
|
|
519
442
|
this.list.addEventListener("scroll", () => {
|
|
520
443
|
if (this.info)
|
|
521
444
|
this.view.requestMeasure(this.placeInfo);
|
|
@@ -553,17 +476,17 @@ class CompletionTooltip {
|
|
|
553
476
|
}
|
|
554
477
|
measureInfo() {
|
|
555
478
|
let sel = this.dom.querySelector("[aria-selected]");
|
|
556
|
-
if (!sel)
|
|
479
|
+
if (!sel || !this.info)
|
|
557
480
|
return null;
|
|
558
|
-
let rect = this.dom.getBoundingClientRect();
|
|
559
|
-
let top = sel.getBoundingClientRect().top - rect.top;
|
|
481
|
+
let rect = this.dom.getBoundingClientRect(), infoRect = this.info.getBoundingClientRect();
|
|
482
|
+
let top = Math.min(sel.getBoundingClientRect().top, innerHeight - infoRect.height) - rect.top;
|
|
560
483
|
if (top < 0 || top > this.list.clientHeight - 10)
|
|
561
484
|
return null;
|
|
562
485
|
let left = this.view.textDirection == Direction.RTL;
|
|
563
486
|
let spaceLeft = rect.left, spaceRight = innerWidth - rect.right;
|
|
564
|
-
if (left && spaceLeft < Math.min(
|
|
487
|
+
if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
|
|
565
488
|
left = false;
|
|
566
|
-
else if (!left && spaceRight < Math.min(
|
|
489
|
+
else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
|
|
567
490
|
left = true;
|
|
568
491
|
return { top, left };
|
|
569
492
|
}
|
|
@@ -574,6 +497,30 @@ class CompletionTooltip {
|
|
|
574
497
|
this.info.classList.toggle("cm-completionInfo-right", !pos.left);
|
|
575
498
|
}
|
|
576
499
|
}
|
|
500
|
+
createListBox(options, id, range) {
|
|
501
|
+
const ul = document.createElement("ul");
|
|
502
|
+
ul.id = id;
|
|
503
|
+
ul.setAttribute("role", "listbox");
|
|
504
|
+
for (let i = range.from; i < range.to; i++) {
|
|
505
|
+
let { completion, match } = options[i];
|
|
506
|
+
const li = ul.appendChild(document.createElement("li"));
|
|
507
|
+
li.id = id + "-" + i;
|
|
508
|
+
li.setAttribute("role", "option");
|
|
509
|
+
let cls = this.optionClass(completion);
|
|
510
|
+
if (cls)
|
|
511
|
+
li.className = cls;
|
|
512
|
+
for (let source of this.optionContent) {
|
|
513
|
+
let node = source(completion, this.view.state, match);
|
|
514
|
+
if (node)
|
|
515
|
+
li.appendChild(node);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
if (range.from)
|
|
519
|
+
ul.classList.add("cm-completionListIncompleteTop");
|
|
520
|
+
if (range.to < options.length)
|
|
521
|
+
ul.classList.add("cm-completionListIncompleteBottom");
|
|
522
|
+
return ul;
|
|
523
|
+
}
|
|
577
524
|
}
|
|
578
525
|
// We allocate a new function instance every time the completion
|
|
579
526
|
// changes to force redrawing/repositioning of the tooltip
|
|
@@ -708,20 +655,24 @@ function sameResults(a, b) {
|
|
|
708
655
|
return false;
|
|
709
656
|
}
|
|
710
657
|
}
|
|
658
|
+
const baseAttrs = {
|
|
659
|
+
"aria-autocomplete": "list",
|
|
660
|
+
"aria-expanded": "false"
|
|
661
|
+
};
|
|
711
662
|
function makeAttrs(id, selected) {
|
|
712
663
|
return {
|
|
713
664
|
"aria-autocomplete": "list",
|
|
665
|
+
"aria-expanded": "true",
|
|
714
666
|
"aria-activedescendant": id + "-" + selected,
|
|
715
|
-
"aria-
|
|
667
|
+
"aria-controls": id
|
|
716
668
|
};
|
|
717
669
|
}
|
|
718
|
-
const
|
|
670
|
+
const none = [];
|
|
719
671
|
function cmpOption(a, b) {
|
|
720
672
|
let dScore = b.match[0] - a.match[0];
|
|
721
673
|
if (dScore)
|
|
722
674
|
return dScore;
|
|
723
|
-
|
|
724
|
-
return lA < lB ? -1 : lA == lB ? 0 : 1;
|
|
675
|
+
return a.completion.label.localeCompare(b.completion.label);
|
|
725
676
|
}
|
|
726
677
|
function getUserEvent(tr) {
|
|
727
678
|
return tr.isUserEvent("input.type") ? "input" : tr.isUserEvent("delete.backward") ? "delete" : null;
|
|
@@ -832,7 +783,7 @@ Accept the current completion.
|
|
|
832
783
|
*/
|
|
833
784
|
const acceptCompletion = (view) => {
|
|
834
785
|
let cState = view.state.field(completionState, false);
|
|
835
|
-
if (!cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin)
|
|
786
|
+
if (view.state.readOnly || !cState || !cState.open || Date.now() - cState.open.timestamp < CompletionInteractMargin)
|
|
836
787
|
return false;
|
|
837
788
|
applyCompletion(view, cState.open.options[cState.open.selected]);
|
|
838
789
|
return true;
|
|
@@ -1008,6 +959,106 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
|
|
|
1008
959
|
}
|
|
1009
960
|
});
|
|
1010
961
|
|
|
962
|
+
const baseTheme = /*@__PURE__*/EditorView.baseTheme({
|
|
963
|
+
".cm-tooltip.cm-tooltip-autocomplete": {
|
|
964
|
+
"& > ul": {
|
|
965
|
+
fontFamily: "monospace",
|
|
966
|
+
whiteSpace: "nowrap",
|
|
967
|
+
overflow: "auto",
|
|
968
|
+
maxWidth_fallback: "700px",
|
|
969
|
+
maxWidth: "min(700px, 95vw)",
|
|
970
|
+
maxHeight: "10em",
|
|
971
|
+
listStyle: "none",
|
|
972
|
+
margin: 0,
|
|
973
|
+
padding: 0,
|
|
974
|
+
"& > li": {
|
|
975
|
+
cursor: "pointer",
|
|
976
|
+
padding: "1px 1em 1px 3px",
|
|
977
|
+
lineHeight: 1.2
|
|
978
|
+
},
|
|
979
|
+
}
|
|
980
|
+
},
|
|
981
|
+
"&light .cm-tooltip-autocomplete ul li[aria-selected]": {
|
|
982
|
+
background: "#39e",
|
|
983
|
+
color: "white",
|
|
984
|
+
},
|
|
985
|
+
"&dark .cm-tooltip-autocomplete ul li[aria-selected]": {
|
|
986
|
+
background: "#347",
|
|
987
|
+
color: "white",
|
|
988
|
+
},
|
|
989
|
+
".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after": {
|
|
990
|
+
content: '"···"',
|
|
991
|
+
opacity: 0.5,
|
|
992
|
+
display: "block",
|
|
993
|
+
textAlign: "center"
|
|
994
|
+
},
|
|
995
|
+
".cm-tooltip.cm-completionInfo": {
|
|
996
|
+
position: "absolute",
|
|
997
|
+
padding: "3px 9px",
|
|
998
|
+
width: "max-content",
|
|
999
|
+
maxWidth: "300px",
|
|
1000
|
+
},
|
|
1001
|
+
".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
|
|
1002
|
+
".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
|
|
1003
|
+
"&light .cm-snippetField": { backgroundColor: "#00000022" },
|
|
1004
|
+
"&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
|
|
1005
|
+
".cm-snippetFieldPosition": {
|
|
1006
|
+
verticalAlign: "text-top",
|
|
1007
|
+
width: 0,
|
|
1008
|
+
height: "1.15em",
|
|
1009
|
+
margin: "0 -0.7px -.7em",
|
|
1010
|
+
borderLeft: "1.4px dotted #888"
|
|
1011
|
+
},
|
|
1012
|
+
".cm-completionMatchedText": {
|
|
1013
|
+
textDecoration: "underline"
|
|
1014
|
+
},
|
|
1015
|
+
".cm-completionDetail": {
|
|
1016
|
+
marginLeft: "0.5em",
|
|
1017
|
+
fontStyle: "italic"
|
|
1018
|
+
},
|
|
1019
|
+
".cm-completionIcon": {
|
|
1020
|
+
fontSize: "90%",
|
|
1021
|
+
width: ".8em",
|
|
1022
|
+
display: "inline-block",
|
|
1023
|
+
textAlign: "center",
|
|
1024
|
+
paddingRight: ".6em",
|
|
1025
|
+
opacity: "0.6"
|
|
1026
|
+
},
|
|
1027
|
+
".cm-completionIcon-function, .cm-completionIcon-method": {
|
|
1028
|
+
"&:after": { content: "'ƒ'" }
|
|
1029
|
+
},
|
|
1030
|
+
".cm-completionIcon-class": {
|
|
1031
|
+
"&:after": { content: "'○'" }
|
|
1032
|
+
},
|
|
1033
|
+
".cm-completionIcon-interface": {
|
|
1034
|
+
"&:after": { content: "'◌'" }
|
|
1035
|
+
},
|
|
1036
|
+
".cm-completionIcon-variable": {
|
|
1037
|
+
"&:after": { content: "'𝑥'" }
|
|
1038
|
+
},
|
|
1039
|
+
".cm-completionIcon-constant": {
|
|
1040
|
+
"&:after": { content: "'𝐶'" }
|
|
1041
|
+
},
|
|
1042
|
+
".cm-completionIcon-type": {
|
|
1043
|
+
"&:after": { content: "'𝑡'" }
|
|
1044
|
+
},
|
|
1045
|
+
".cm-completionIcon-enum": {
|
|
1046
|
+
"&:after": { content: "'∪'" }
|
|
1047
|
+
},
|
|
1048
|
+
".cm-completionIcon-property": {
|
|
1049
|
+
"&:after": { content: "'□'" }
|
|
1050
|
+
},
|
|
1051
|
+
".cm-completionIcon-keyword": {
|
|
1052
|
+
"&:after": { content: "'🔑\uFE0E'" } // Disable emoji rendering
|
|
1053
|
+
},
|
|
1054
|
+
".cm-completionIcon-namespace": {
|
|
1055
|
+
"&:after": { content: "'▢'" }
|
|
1056
|
+
},
|
|
1057
|
+
".cm-completionIcon-text": {
|
|
1058
|
+
"&:after": { content: "'abc'", fontSize: "50%", verticalAlign: "middle" }
|
|
1059
|
+
}
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1011
1062
|
class FieldPos {
|
|
1012
1063
|
constructor(field, line, from, to) {
|
|
1013
1064
|
this.field = field;
|
|
@@ -1053,7 +1104,7 @@ class Snippet {
|
|
|
1053
1104
|
let lines = [], positions = [], m;
|
|
1054
1105
|
for (let line of template.split(/\r\n?|\n/)) {
|
|
1055
1106
|
while (m = /[#$]\{(?:(\d+)(?::([^}]*))?|([^}]*))\}/.exec(line)) {
|
|
1056
|
-
let seq = m[1] ? +m[1] : null, name = m[2] || m[3], found = -1;
|
|
1107
|
+
let seq = m[1] ? +m[1] : null, name = m[2] || m[3] || "", found = -1;
|
|
1057
1108
|
for (let i = 0; i < fields.length; i++) {
|
|
1058
1109
|
if (seq != null ? fields[i].seq == seq : name ? fields[i].name == name : false)
|
|
1059
1110
|
found = i;
|
|
@@ -1062,7 +1113,7 @@ class Snippet {
|
|
|
1062
1113
|
let i = 0;
|
|
1063
1114
|
while (i < fields.length && (seq == null || (fields[i].seq != null && fields[i].seq < seq)))
|
|
1064
1115
|
i++;
|
|
1065
|
-
fields.splice(i, 0, { seq, name
|
|
1116
|
+
fields.splice(i, 0, { seq, name });
|
|
1066
1117
|
found = i;
|
|
1067
1118
|
for (let pos of positions)
|
|
1068
1119
|
if (pos.field >= found)
|
|
@@ -1158,8 +1209,7 @@ function snippet(template) {
|
|
|
1158
1209
|
let active = new ActiveSnippet(ranges, 0);
|
|
1159
1210
|
let effects = spec.effects = [setActive.of(active)];
|
|
1160
1211
|
if (editor.state.field(snippetState, false) === undefined)
|
|
1161
|
-
effects.push(StateEffect.appendConfig.of([snippetState
|
|
1162
|
-
snippetPointerHandler, baseTheme]));
|
|
1212
|
+
effects.push(StateEffect.appendConfig.of([snippetState, addSnippetKeymap, snippetPointerHandler, baseTheme]));
|
|
1163
1213
|
}
|
|
1164
1214
|
editor.dispatch(editor.state.update(spec));
|
|
1165
1215
|
};
|
|
@@ -1209,7 +1259,7 @@ to [`clearSnippet`](https://codemirror.net/6/docs/ref/#autocomplete.clearSnippet
|
|
|
1209
1259
|
const snippetKeymap = /*@__PURE__*/Facet.define({
|
|
1210
1260
|
combine(maps) { return maps.length ? maps[0] : defaultSnippetKeymap; }
|
|
1211
1261
|
});
|
|
1212
|
-
const addSnippetKeymap = /*@__PURE__*/Prec.
|
|
1262
|
+
const addSnippetKeymap = /*@__PURE__*/Prec.highest(/*@__PURE__*/keymap.compute([snippetKeymap], state => state.facet(snippetKeymap)));
|
|
1213
1263
|
/**
|
|
1214
1264
|
Create a completion from a snippet. Returns an object with the
|
|
1215
1265
|
properties from `completion`, plus an `apply` function that
|
|
@@ -1343,7 +1393,7 @@ const completionKeymap = [
|
|
|
1343
1393
|
{ key: "PageUp", run: /*@__PURE__*/moveCompletionSelection(false, "page") },
|
|
1344
1394
|
{ key: "Enter", run: acceptCompletion }
|
|
1345
1395
|
];
|
|
1346
|
-
const completionKeymapExt = /*@__PURE__*/Prec.
|
|
1396
|
+
const completionKeymapExt = /*@__PURE__*/Prec.highest(/*@__PURE__*/keymap.computeN([completionConfig], state => state.facet(completionConfig).defaultKeymap ? [completionKeymap] : []));
|
|
1347
1397
|
/**
|
|
1348
1398
|
Get the current completion status. When completions are available,
|
|
1349
1399
|
this will return `"active"`. When completions are pending (in the
|
|
@@ -1363,5 +1413,13 @@ function currentCompletions(state) {
|
|
|
1363
1413
|
let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
|
|
1364
1414
|
return open ? open.options.map(o => o.completion) : [];
|
|
1365
1415
|
}
|
|
1416
|
+
/**
|
|
1417
|
+
Return the currently selected completion, if any.
|
|
1418
|
+
*/
|
|
1419
|
+
function selectedCompletion(state) {
|
|
1420
|
+
var _a;
|
|
1421
|
+
let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
|
|
1422
|
+
return open ? open.options[open.selected].completion : null;
|
|
1423
|
+
}
|
|
1366
1424
|
|
|
1367
|
-
export { CompletionContext, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifIn, ifNotIn, moveCompletionSelection, nextSnippetField, prevSnippetField, snippet, snippetCompletion, snippetKeymap, startCompletion };
|
|
1425
|
+
export { CompletionContext, acceptCompletion, autocompletion, clearSnippet, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, ifIn, ifNotIn, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codemirror/autocomplete",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.6",
|
|
4
4
|
"description": "Autocompletion for the CodeMirror code editor",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "cm-runtests",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@codemirror/language": "^0.19.0",
|
|
30
|
-
"@codemirror/state": "^0.19.
|
|
30
|
+
"@codemirror/state": "^0.19.4",
|
|
31
31
|
"@codemirror/text": "^0.19.2",
|
|
32
32
|
"@codemirror/tooltip": "^0.19.0",
|
|
33
33
|
"@codemirror/view": "^0.19.0",
|