1
0
Fork 0
mirror of https://github.com/DanielnetoDotCom/YouPHPTube synced 2025-10-05 19:42:38 +02:00

npm update

This commit is contained in:
Daniel Neto 2023-08-12 10:32:47 -03:00
parent 0cdd3e9fee
commit 4696ba952f
1437 changed files with 32727 additions and 1248226 deletions

View file

@ -1,3 +1,83 @@
## 6.9.0 (2023-07-18)
### New features
Completions may now provide a `displayLabel` property that overrides the way they are displayed in the completion list.
## 6.8.1 (2023-06-23)
### Bug fixes
`acceptCompletion` now returns false (allowing other handlers to take effect) when the completion popup is open but disabled.
## 6.8.0 (2023-06-12)
### New features
The result of `Completion.info` may now include a `destroy` method that will be called when the tooltip is removed.
## 6.7.1 (2023-05-13)
### Bug fixes
Fix a bug that cause incorrect ordering of completions when some results covered input text and others didn't.
## 6.7.0 (2023-05-11)
### New features
The new `hasNextSnippetField` and `hasPrevSnippetField` functions can be used to figure out if the snippet-field-motion commands apply to a given state.
## 6.6.1 (2023-05-03)
### Bug fixes
Fix a bug that made the editor use the completion's original position, rather than its current position, when changes happened in the document while a result was active.
## 6.6.0 (2023-04-27)
### Bug fixes
Fix a bug in `insertCompletionText` that caused it to replace the wrong range when a result set's `to` fell after the cursor.
### New features
Functions returned by `snippet` can now be called without a completion object.
## 6.5.1 (2023-04-13)
### Bug fixes
Keep completions open when interaction with an info tooltip moves focus out of the editor.
## 6.5.0 (2023-04-13)
### Bug fixes
When `closeBrackets` skips a bracket, it now generates a change that overwrites the bracket.
Replace the entire selected range when picking a completion with a non-cursor selection active.
### New features
Completions can now provide a `section` field that is used to group them into sections.
The new `positionInfo` option can be used to provide custom logic for positioning the info tooltips.
## 6.4.2 (2023-02-17)
### Bug fixes
Fix a bug where the apply method created by `snippet` didn't add a `pickedCompletion` annotation to the transactions it created.
## 6.4.1 (2023-02-14)
### Bug fixes
Don't consider node names in trees that aren't the same language as the one at the completion position in `ifIn` and `ifNotIn`.
Make sure completions that exactly match the input get a higher score than those that don't (so that even if the latter has a score boost, it ends up lower in the list).
## 6.4.0 (2022-12-14)
### Bug fixes

View file

@ -1,6 +1,6 @@
MIT License
Copyright (C) 2018-2021 by Marijn Haverbeke <marijnh@gmail.com> and others
Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -112,9 +112,12 @@ cursor is in a syntax node with one of the given names.
*/
function ifIn(nodes, source) {
return (context) => {
for (let pos = language.syntaxTree(context.state).resolveInner(context.pos, -1); pos; pos = pos.parent)
for (let pos = language.syntaxTree(context.state).resolveInner(context.pos, -1); pos; pos = pos.parent) {
if (nodes.indexOf(pos.name) > -1)
return source(context);
if (pos.type.isTop)
break;
}
return null;
};
}
@ -124,20 +127,24 @@ cursor is in a syntax node with one of the given names.
*/
function ifNotIn(nodes, source) {
return (context) => {
for (let pos = language.syntaxTree(context.state).resolveInner(context.pos, -1); pos; pos = pos.parent)
for (let pos = language.syntaxTree(context.state).resolveInner(context.pos, -1); pos; pos = pos.parent) {
if (nodes.indexOf(pos.name) > -1)
return null;
if (pos.type.isTop)
break;
}
return source(context);
};
}
class Option {
constructor(completion, source, match) {
constructor(completion, source, match, score) {
this.completion = completion;
this.source = source;
this.match = match;
this.score = score;
}
}
function cur(state) { return state.selection.main.head; }
function cur(state) { return state.selection.main.from; }
// Make sure the given regexp has a $ at its end and, if `start` is
// true, a ^ at its start.
function ensureAnchor(expr, start) {
@ -159,30 +166,17 @@ completion's text in the main selection range, and any other
selection range that has the same text in front of it.
*/
function insertCompletionText(state$1, text, from, to) {
let { main } = state$1.selection, fromOff = from - main.from, toOff = to - main.from;
return Object.assign(Object.assign({}, state$1.changeByRange(range => {
if (range == state$1.selection.main)
return {
changes: { from: from, to: to, insert: text },
range: state.EditorSelection.cursor(from + text.length)
};
let len = to - from;
if (!range.empty ||
len && state$1.sliceDoc(range.from - len, range.from) != state$1.sliceDoc(from, to))
if (range != main && from != to &&
state$1.sliceDoc(range.from + fromOff, range.from + toOff) != state$1.sliceDoc(from, to))
return { range };
return {
changes: { from: range.from - len, to: range.from, insert: text },
range: state.EditorSelection.cursor(range.from - len + text.length)
changes: { from: range.from + fromOff, to: to == main.from ? range.to : range.from + toOff, insert: text },
range: state.EditorSelection.cursor(range.from + fromOff + text.length)
};
})), { userEvent: "input.complete" });
}
function applyCompletion(view, option) {
const apply = option.completion.apply || option.completion.label;
let result = option.source;
if (typeof apply == "string")
view.dispatch(Object.assign(Object.assign({}, insertCompletionText(view.state, apply, result.from, result.to)), { annotations: pickedCompletion.of(option.completion) }));
else
apply(view, option.completion, result.from, result.to);
}
const SourceCache = new WeakMap();
function asSource(source) {
if (!Array.isArray(source))
@ -192,6 +186,8 @@ function asSource(source) {
SourceCache.set(source, known = completeFromList(source));
return known;
}
const startCompletionEffect = state.StateEffect.define();
const closeCompletionEffect = state.StateEffect.define();
// A pattern matcher for fuzzy completion matching. Create an instance
// once for a pattern, and then use that to match any number of
@ -206,6 +202,8 @@ class FuzzyMatcher {
this.any = [];
this.precise = [];
this.byWord = [];
this.score = 0;
this.matched = [];
for (let p = 0; p < pattern.length;) {
let char = state.codePointAt(pattern, p), size = state.codePointSize(char);
this.chars.push(char);
@ -215,29 +213,39 @@ class FuzzyMatcher {
}
this.astral = pattern.length != this.chars.length;
}
ret(score, matched) {
this.score = score;
this.matched = matched;
return true;
}
// Matches a given word (completion) against the pattern (input).
// Will return null for no match, and otherwise an array that starts
// with the match score, followed by any number of `from, to` pairs
// indicating the matched parts of `word`.
// Will return a boolean indicating whether there was a match and,
// on success, set `this.score` to the score, `this.matched` to an
// array of `from, to` pairs indicating the matched parts of `word`.
//
// The score is a number that is more negative the worse the match
// is. See `Penalty` above.
match(word) {
if (this.pattern.length == 0)
return [0];
return this.ret(-100 /* NotFull */, []);
if (word.length < this.pattern.length)
return null;
return false;
let { chars, folded, any, precise, byWord } = this;
// For single-character queries, only match when they occur right
// at the start
if (chars.length == 1) {
let first = state.codePointAt(word, 0);
return first == chars[0] ? [0, 0, state.codePointSize(first)]
: first == folded[0] ? [-200 /* Penalty.CaseFold */, 0, state.codePointSize(first)] : null;
let first = state.codePointAt(word, 0), firstSize = state.codePointSize(first);
let score = firstSize == word.length ? 0 : -100 /* NotFull */;
if (first == chars[0]) ;
else if (first == folded[0])
score += -200 /* CaseFold */;
else
return false;
return this.ret(score, [0, firstSize]);
}
let direct = word.indexOf(this.pattern);
if (direct == 0)
return [0, 0, this.pattern.length];
return this.ret(word.length == this.pattern.length ? 0 : -100 /* NotFull */, [0, this.pattern.length]);
let len = chars.length, anyTo = 0;
if (direct < 0) {
for (let i = 0, e = Math.min(word.length, 200); i < e && anyTo < len;) {
@ -248,7 +256,7 @@ class FuzzyMatcher {
}
// No match, exit immediately
if (anyTo < len)
return null;
return false;
}
// This tracks the extent of the precise (non-folded, not
// necessarily adjacent) match
@ -261,7 +269,7 @@ class FuzzyMatcher {
let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
let hasLower = /[a-z]/.test(word), wordAdjacent = true;
// Go over the option's text, scanning for the various kinds of matches
for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* Tp.NonWord */; i < e && byWordTo < len;) {
for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* NonWord */; i < e && byWordTo < len;) {
let next = state.codePointAt(word, i);
if (direct < 0) {
if (preciseTo < len && next == chars[preciseTo])
@ -279,9 +287,9 @@ class FuzzyMatcher {
}
}
let ch, type = next < 0xff
? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Tp.Lower */ : next >= 65 && next <= 90 ? 1 /* Tp.Upper */ : 0 /* Tp.NonWord */)
: ((ch = state.fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Tp.Upper */ : ch != ch.toUpperCase() ? 2 /* Tp.Lower */ : 0 /* Tp.NonWord */);
if (!i || type == 1 /* Tp.Upper */ && hasLower || prevType == 0 /* Tp.NonWord */ && type != 0 /* Tp.NonWord */) {
? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Lower */ : next >= 65 && next <= 90 ? 1 /* Upper */ : 0 /* NonWord */)
: ((ch = state.fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Upper */ : ch != ch.toUpperCase() ? 2 /* Lower */ : 0 /* NonWord */);
if (!i || type == 1 /* Upper */ && hasLower || prevType == 0 /* NonWord */ && type != 0 /* NonWord */) {
if (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
byWord[byWordTo++] = i;
else if (byWord.length)
@ -291,30 +299,31 @@ class FuzzyMatcher {
i += state.codePointSize(next);
}
if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0), byWord, word);
return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0), byWord, word);
if (adjacentTo == len && adjacentStart == 0)
return [-200 /* Penalty.CaseFold */ - word.length, 0, adjacentEnd];
return this.ret(-200 /* CaseFold */ - word.length + (adjacentEnd == word.length ? 0 : -100 /* NotFull */), [0, adjacentEnd]);
if (direct > -1)
return [-700 /* Penalty.NotStart */ - word.length, direct, direct + this.pattern.length];
return this.ret(-700 /* NotStart */ - word.length, [direct, direct + this.pattern.length]);
if (adjacentTo == len)
return [-200 /* Penalty.CaseFold */ + -700 /* Penalty.NotStart */ - word.length, adjacentStart, adjacentEnd];
return this.ret(-200 /* CaseFold */ + -700 /* NotStart */ - word.length, [adjacentStart, adjacentEnd]);
if (byWordTo == len)
return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0) + -700 /* Penalty.NotStart */ +
(wordAdjacent ? 0 : -1100 /* Penalty.Gap */), byWord, word);
return chars.length == 2 ? null : this.result((any[0] ? -700 /* Penalty.NotStart */ : 0) + -200 /* Penalty.CaseFold */ + -1100 /* Penalty.Gap */, any, word);
return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */ +
(wordAdjacent ? 0 : -1100 /* Gap */), byWord, word);
return chars.length == 2 ? false
: this.result((any[0] ? -700 /* NotStart */ : 0) + -200 /* CaseFold */ + -1100 /* Gap */, any, word);
}
result(score, positions, word) {
let result = [score - word.length], i = 1;
let result = [], i = 0;
for (let pos of positions) {
let to = pos + (this.astral ? state.codePointSize(state.codePointAt(word, pos)) : 1);
if (i > 1 && result[i - 1] == pos)
if (i && result[i - 1] == pos)
result[i - 1] = to;
else {
result[i++] = pos;
result[i++] = to;
}
}
return result;
return this.ret(score - word.length, result);
}
}
@ -332,6 +341,7 @@ const completionConfig = state.Facet.define({
aboveCursor: false,
icons: true,
addToOptions: [],
positionInfo: defaultPositionInfo,
compareCompletions: (a, b) => a.label.localeCompare(b.label),
interactionDelay: 75
}, {
@ -347,6 +357,36 @@ const completionConfig = state.Facet.define({
function joinClass(a, b) {
return a ? b ? a + " " + b : a : b;
}
function defaultPositionInfo(view$1, list, option, info, space) {
let rtl = view$1.textDirection == view.Direction.RTL, left = rtl, narrow = false;
let side = "top", offset, maxWidth;
let spaceLeft = list.left - space.left, spaceRight = space.right - list.right;
let infoWidth = info.right - info.left, infoHeight = info.bottom - info.top;
if (left && spaceLeft < Math.min(infoWidth, spaceRight))
left = false;
else if (!left && spaceRight < Math.min(infoWidth, spaceLeft))
left = true;
if (infoWidth <= (left ? spaceLeft : spaceRight)) {
offset = Math.max(space.top, Math.min(option.top, space.bottom - infoHeight)) - list.top;
maxWidth = Math.min(400 /* Width */, left ? spaceLeft : spaceRight);
}
else {
narrow = true;
maxWidth = Math.min(400 /* Width */, (rtl ? list.right : space.right - list.left) - 30 /* Margin */);
let spaceBelow = space.bottom - list.bottom;
if (spaceBelow >= infoHeight || spaceBelow > list.top) { // Below the completion
offset = option.bottom - list.top;
}
else { // Above it
side = "bottom";
offset = list.bottom - option.top;
}
}
return {
style: `${side}: ${offset}px; max-width: ${maxWidth}px`,
class: "cm-completionInfo-" + (narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right")
};
}
function optionContent(config) {
let content = config.addToOptions.slice();
@ -366,8 +406,8 @@ function optionContent(config) {
render(completion, _s, match) {
let labelElt = document.createElement("span");
labelElt.className = "cm-completionLabel";
let { label } = completion, off = 0;
for (let j = 1; j < match.length;) {
let label = completion.displayLabel || completion.label, off = 0;
for (let j = 0; j < match.length;) {
let from = match[j++], to = match[j++];
if (from > off)
labelElt.appendChild(document.createTextNode(label.slice(off, from)));
@ -407,13 +447,15 @@ function rangeAroundSelected(total, selected, max) {
return { from: total - (off + 1) * max, to: total - off * max };
}
class CompletionTooltip {
constructor(view, stateField) {
constructor(view, stateField, applyCompletion) {
this.view = view;
this.stateField = stateField;
this.applyCompletion = applyCompletion;
this.info = null;
this.placeInfo = {
this.infoDestroy = null;
this.placeInfoReq = {
read: () => this.measureInfo(),
write: (pos) => this.positionInfo(pos),
write: (pos) => this.placeInfo(pos),
key: this
};
this.space = null;
@ -431,16 +473,22 @@ class CompletionTooltip {
this.dom.addEventListener("mousedown", (e) => {
for (let dom = e.target, match; dom && dom != this.dom; dom = dom.parentNode) {
if (dom.nodeName == "LI" && (match = /-(\d+)$/.exec(dom.id)) && +match[1] < options.length) {
applyCompletion(view, options[+match[1]]);
this.applyCompletion(view, options[+match[1]]);
e.preventDefault();
return;
}
}
});
this.dom.addEventListener("focusout", (e) => {
let state = view.state.field(this.stateField, false);
if (state && state.tooltip && view.state.facet(completionConfig).closeOnBlur &&
e.relatedTarget != view.contentDOM)
view.dispatch({ effects: closeCompletionEffect.of(null) });
});
this.list = this.dom.appendChild(this.createListBox(options, cState.id, this.range));
this.list.addEventListener("scroll", () => {
if (this.info)
this.view.requestMeasure(this.placeInfo);
this.view.requestMeasure(this.placeInfoReq);
});
}
mount() { this.updateSel(); }
@ -470,7 +518,7 @@ class CompletionTooltip {
positioned(space) {
this.space = space;
if (this.info)
this.view.requestMeasure(this.placeInfo);
this.view.requestMeasure(this.placeInfoReq);
}
updateSel() {
let cState = this.view.state.field(this.stateField), open = cState.open;
@ -480,43 +528,52 @@ class CompletionTooltip {
this.list = this.dom.appendChild(this.createListBox(open.options, cState.id, this.range));
this.list.addEventListener("scroll", () => {
if (this.info)
this.view.requestMeasure(this.placeInfo);
this.view.requestMeasure(this.placeInfoReq);
});
}
if (this.updateSelectedOption(open.selected)) {
if (this.info) {
this.info.remove();
this.info = null;
}
this.destroyInfo();
let { completion } = open.options[open.selected];
let { info } = completion;
if (!info)
return;
let infoResult = typeof info === 'string' ? document.createTextNode(info) : info(completion);
let infoResult = typeof info === "string" ? document.createTextNode(info) : info(completion);
if (!infoResult)
return;
if ('then' in infoResult) {
infoResult.then(node => {
if (node && this.view.state.field(this.stateField, false) == cState)
this.addInfoPane(node);
if ("then" in infoResult) {
infoResult.then(obj => {
if (obj && this.view.state.field(this.stateField, false) == cState)
this.addInfoPane(obj, completion);
}).catch(e => view.logException(this.view.state, e, "completion info"));
}
else {
this.addInfoPane(infoResult);
this.addInfoPane(infoResult, completion);
}
}
}
addInfoPane(content) {
let dom = this.info = document.createElement("div");
dom.className = "cm-tooltip cm-completionInfo";
dom.appendChild(content);
this.dom.appendChild(dom);
this.view.requestMeasure(this.placeInfo);
addInfoPane(content, completion) {
this.destroyInfo();
let wrap = this.info = document.createElement("div");
wrap.className = "cm-tooltip cm-completionInfo";
if (content.nodeType != null) {
wrap.appendChild(content);
this.infoDestroy = null;
}
else {
let { dom, destroy } = content;
wrap.appendChild(dom);
this.infoDestroy = destroy || null;
}
this.dom.appendChild(wrap);
this.view.requestMeasure(this.placeInfoReq);
}
updateSelectedOption(selected) {
let set = null;
for (let opt = this.list.firstChild, i = this.range.from; opt; opt = opt.nextSibling, i++) {
if (i == selected) {
if (opt.nodeName != "LI" || !opt.id) {
i--; // A section header
}
else if (i == selected) {
if (!opt.hasAttribute("aria-selected")) {
opt.setAttribute("aria-selected", "true");
set = opt;
@ -546,41 +603,17 @@ class CompletionTooltip {
if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 ||
selRect.bottom < Math.max(space.top, listRect.top) + 10)
return null;
let rtl = this.view.textDirection == view.Direction.RTL, left = rtl, narrow = false, maxWidth;
let top = "", bottom = "";
let spaceLeft = listRect.left - space.left, spaceRight = space.right - listRect.right;
if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
left = false;
else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
left = true;
if (infoRect.width <= (left ? spaceLeft : spaceRight)) {
top = (Math.max(space.top, Math.min(selRect.top, space.bottom - infoRect.height)) - listRect.top) + "px";
maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight) + "px";
}
else {
narrow = true;
maxWidth = Math.min(400 /* Info.Width */, (rtl ? listRect.right : space.right - listRect.left) - 30 /* Info.Margin */) + "px";
let spaceBelow = space.bottom - listRect.bottom;
if (spaceBelow >= infoRect.height || spaceBelow > listRect.top) // Below the completion
top = (selRect.bottom - listRect.top) + "px";
else // Above it
bottom = (listRect.bottom - selRect.top) + "px";
}
return {
top, bottom, maxWidth,
class: narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right",
};
return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space);
}
positionInfo(pos) {
placeInfo(pos) {
if (this.info) {
if (pos) {
this.info.style.top = pos.top;
this.info.style.bottom = pos.bottom;
this.info.style.maxWidth = pos.maxWidth;
this.info.className = "cm-tooltip cm-completionInfo cm-completionInfo-" + pos.class;
if (pos.style)
this.info.style.cssText = pos.style;
this.info.className = "cm-tooltip cm-completionInfo " + (pos.class || "");
}
else {
this.info.style.top = "-1e6px";
this.info.style.cssText = "top: -1e6px";
}
}
}
@ -590,8 +623,22 @@ class CompletionTooltip {
ul.setAttribute("role", "listbox");
ul.setAttribute("aria-expanded", "true");
ul.setAttribute("aria-label", this.view.state.phrase("Completions"));
let curSection = null;
for (let i = range.from; i < range.to; i++) {
let { completion, match } = options[i];
let { completion, match } = options[i], { section } = completion;
if (section) {
let name = typeof section == "string" ? section : section.name;
if (name != curSection && (i > range.from || range.from == 0)) {
curSection = name;
if (typeof section != "string" && section.header) {
ul.appendChild(section.header(section));
}
else {
let header = ul.appendChild(document.createElement("completion-section"));
header.textContent = name;
}
}
}
const li = ul.appendChild(document.createElement("li"));
li.id = id + "-" + i;
li.setAttribute("role", "option");
@ -610,11 +657,22 @@ class CompletionTooltip {
ul.classList.add("cm-completionListIncompleteBottom");
return ul;
}
destroyInfo() {
if (this.info) {
if (this.infoDestroy)
this.infoDestroy();
this.info.remove();
this.info = null;
}
}
destroy() {
this.destroyInfo();
}
}
// We allocate a new function instance every time the completion
// changes to force redrawing/repositioning of the tooltip
function completionTooltip(stateField) {
return (view) => new CompletionTooltip(view, stateField);
function completionTooltip(stateField, applyCompletion) {
return (view) => new CompletionTooltip(view, stateField, applyCompletion);
}
function scrollIntoView(container, element) {
let parent = container.getBoundingClientRect();
@ -632,35 +690,56 @@ function score(option) {
(option.type ? 1 : 0);
}
function sortOptions(active, state) {
let options = [], i = 0;
let options = [];
let sections = null;
let addOption = (option) => {
options.push(option);
let { section } = option.completion;
if (section) {
if (!sections)
sections = [];
let name = typeof section == "string" ? section : section.name;
if (!sections.some(s => s.name == name))
sections.push(typeof section == "string" ? { name } : section);
}
};
for (let a of active)
if (a.hasResult()) {
let getMatch = a.result.getMatch;
if (a.result.filter === false) {
let getMatch = a.result.getMatch;
for (let option of a.result.options) {
let match = [1e9 - i++];
if (getMatch)
for (let n of getMatch(option))
match.push(n);
options.push(new Option(option, a, match));
addOption(new Option(option, a.source, getMatch ? getMatch(option) : [], 1e9 - options.length));
}
}
else {
let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match;
let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to));
for (let option of a.result.options)
if (match = matcher.match(option.label)) {
if (option.boost != null)
match[0] += option.boost;
options.push(new Option(option, a, match));
if (matcher.match(option.label)) {
let matched = !option.displayLabel ? matcher.matched : getMatch ? getMatch(option, matcher.matched) : [];
addOption(new Option(option, a.source, matched, matcher.score + (option.boost || 0)));
}
}
}
if (sections) {
let sectionOrder = Object.create(null), pos = 0;
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); };
for (let s of sections.sort(cmp)) {
pos -= 1e5;
sectionOrder[s.name] = pos;
}
for (let option of options) {
let { section } = option.completion;
if (section)
option.score += sectionOrder[typeof section == "string" ? section : section.name];
}
}
let result = [], prev = null;
let compare = state.facet(completionConfig).compareCompletions;
for (let opt of options.sort((a, b) => (b.match[0] - a.match[0]) || compare(a.completion, b.completion))) {
if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail ||
(prev.type != null && opt.completion.type != null && prev.type != opt.completion.type) ||
prev.apply != opt.completion.apply)
for (let opt of options.sort((a, b) => (b.score - a.score) || compare(a.completion, b.completion))) {
let cur = opt.completion;
if (!prev || prev.label != cur.label || prev.detail != cur.detail ||
(prev.type != null && cur.type != null && prev.type != cur.type) ||
prev.apply != cur.apply || prev.boost != cur.boost)
result.push(opt);
else if (score(opt.completion) > score(prev))
result[result.length - 1] = opt;
@ -684,7 +763,7 @@ class CompletionDialog {
static build(active, state, id, prev, conf) {
let options = sortOptions(active, state);
if (!options.length) {
return prev && active.some(a => a.state == 1 /* State.Pending */) ?
return prev && active.some(a => a.state == 1 /* Pending */) ?
new CompletionDialog(prev.options, prev.attrs, prev.tooltip, prev.timestamp, prev.selected, true) : null;
}
let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
@ -698,7 +777,7 @@ class CompletionDialog {
}
return new CompletionDialog(options, makeAttrs(id, selected), {
pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
create: completionTooltip(completionState),
create: completionTooltip(completionState, applyCompletion),
above: conf.aboveCursor,
}, prev ? prev.timestamp : Date.now(), selected, false);
}
@ -721,7 +800,7 @@ class CompletionState {
state.languageDataAt("autocomplete", cur(state)).map(asSource);
let active = sources.map(source => {
let value = this.active.find(s => s.source == source) ||
new ActiveSource(source, this.active.some(a => a.state != 0 /* State.Inactive */) ? 1 /* State.Pending */ : 0 /* State.Inactive */);
new ActiveSource(source, this.active.some(a => a.state != 0 /* Inactive */) ? 1 /* Pending */ : 0 /* Inactive */);
return value.update(tr, conf);
});
if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
@ -732,10 +811,10 @@ class CompletionState {
if (tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
!sameResults(active, this.active))
open = CompletionDialog.build(active, state, this.id, open, conf);
else if (open && open.disabled && !active.some(a => a.state == 1 /* State.Pending */))
else if (open && open.disabled && !active.some(a => a.state == 1 /* Pending */))
open = null;
if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult()))
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* State.Inactive */) : a);
if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
for (let effect of tr.effects)
if (effect.is(setSelectedEffect))
open = open && open.setSelected(effect.value, this.id);
@ -789,13 +868,13 @@ class ActiveSource {
value = value.handleUserEvent(tr, event, conf);
else if (tr.docChanged)
value = value.handleChange(tr);
else if (tr.selection && value.state != 0 /* State.Inactive */)
value = new ActiveSource(value.source, 0 /* State.Inactive */);
else if (tr.selection && value.state != 0 /* Inactive */)
value = new ActiveSource(value.source, 0 /* Inactive */);
for (let effect of tr.effects) {
if (effect.is(startCompletionEffect))
value = new ActiveSource(value.source, 1 /* State.Pending */, effect.value ? cur(tr.state) : -1);
value = new ActiveSource(value.source, 1 /* Pending */, effect.value ? cur(tr.state) : -1);
else if (effect.is(closeCompletionEffect))
value = new ActiveSource(value.source, 0 /* State.Inactive */);
value = new ActiveSource(value.source, 0 /* Inactive */);
else if (effect.is(setActiveEffect))
for (let active of effect.value)
if (active.source == value.source)
@ -804,10 +883,10 @@ class ActiveSource {
return value;
}
handleUserEvent(tr, type, conf) {
return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* State.Pending */);
return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* Pending */);
}
handleChange(tr) {
return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
}
map(changes) {
return changes.empty || this.explicitPos < 0 ? this : new ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos));
@ -815,7 +894,7 @@ class ActiveSource {
}
class ActiveResult extends ActiveSource {
constructor(source, explicitPos, result, from, to) {
super(source, 2 /* State.Result */, explicitPos);
super(source, 2 /* Result */, explicitPos);
this.result = result;
this.from = from;
this.to = to;
@ -828,17 +907,17 @@ class ActiveResult extends ActiveSource {
if ((this.explicitPos < 0 ? pos <= from : pos < this.from) ||
pos > to ||
type == "delete" && cur(tr.startState) == this.from)
return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* State.Pending */ : 0 /* State.Inactive */);
return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */);
let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos), updated;
if (checkValid(this.result.validFor, tr.state, from, to))
return new ActiveResult(this.source, explicitPos, this.result, from, to);
if (this.result.update &&
(updated = this.result.update(this.result, from, to, new CompletionContext(tr.state, pos, explicitPos >= 0))))
return new ActiveResult(this.source, explicitPos, updated, updated.from, (_a = updated.to) !== null && _a !== void 0 ? _a : cur(tr.state));
return new ActiveSource(this.source, 1 /* State.Pending */, explicitPos);
return new ActiveSource(this.source, 1 /* Pending */, explicitPos);
}
handleChange(tr) {
return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
}
map(mapping) {
return mapping.empty ? this :
@ -851,8 +930,6 @@ function checkValid(validFor, state, from, to) {
let text = state.sliceDoc(from, to);
return typeof validFor == "function" ? validFor(text, from, to, state) : ensureAnchor(validFor, true).test(text);
}
const startCompletionEffect = state.StateEffect.define();
const closeCompletionEffect = state.StateEffect.define();
const setActiveEffect = state.StateEffect.define({
map(sources, mapping) { return sources.map(s => s.map(mapping)); }
});
@ -865,6 +942,17 @@ const completionState = state.StateField.define({
view.EditorView.contentAttributes.from(f, state => state.attrs)
]
});
function applyCompletion(view, option) {
const apply = option.completion.apply || option.completion.label;
let result = view.state.field(completionState).active.find(a => a.source == option.source);
if (!(result instanceof ActiveResult))
return false;
if (typeof apply == "string")
view.dispatch(Object.assign(Object.assign({}, insertCompletionText(view.state, apply, result.from, result.to)), { annotations: pickedCompletion.of(option.completion) }));
else
apply(view, option.completion, result.from, result.to);
return true;
}
/**
Returns a command that moves the completion selection forward or
@ -895,12 +983,10 @@ Accept the current completion.
*/
const acceptCompletion = (view) => {
let cState = view.state.field(completionState, false);
if (view.state.readOnly || !cState || !cState.open || cState.open.selected < 0 ||
if (view.state.readOnly || !cState || !cState.open || cState.open.selected < 0 || cState.open.disabled ||
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
return false;
if (!cState.open.disabled)
applyCompletion(view, cState.open.options[cState.open.selected]);
return true;
return applyCompletion(view, cState.open.options[cState.open.selected]);
};
/**
Explicitly start autocompletion.
@ -917,7 +1003,7 @@ Close the currently active completion.
*/
const closeCompletion = (view) => {
let cState = view.state.field(completionState, false);
if (!cState || !cState.active.some(a => a.state != 0 /* State.Inactive */))
if (!cState || !cState.active.some(a => a.state != 0 /* Inactive */))
return false;
view.dispatch({ effects: closeCompletionEffect.of(null) });
return true;
@ -940,9 +1026,9 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
this.debounceUpdate = -1;
this.running = [];
this.debounceAccept = -1;
this.composing = 0 /* CompositionState.None */;
this.composing = 0 /* None */;
for (let active of view.state.field(completionState).active)
if (active.state == 1 /* State.Pending */)
if (active.state == 1 /* Pending */)
this.startQuery(active);
}
update(update) {
@ -973,21 +1059,21 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
}
if (this.debounceUpdate > -1)
clearTimeout(this.debounceUpdate);
this.debounceUpdate = cState.active.some(a => a.state == 1 /* State.Pending */ && !this.running.some(q => q.active.source == a.source))
this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.active.source == a.source))
? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
if (this.composing != 0 /* CompositionState.None */)
if (this.composing != 0 /* None */)
for (let tr of update.transactions) {
if (getUserEvent(tr) == "input")
this.composing = 2 /* CompositionState.Changed */;
else if (this.composing == 2 /* CompositionState.Changed */ && tr.selection)
this.composing = 3 /* CompositionState.ChangedAndMoved */;
this.composing = 2 /* Changed */;
else if (this.composing == 2 /* Changed */ && tr.selection)
this.composing = 3 /* ChangedAndMoved */;
}
}
startUpdate() {
this.debounceUpdate = -1;
let { state } = this.view, cState = state.field(completionState);
for (let active of cState.active) {
if (active.state == 1 /* State.Pending */ && !this.running.some(r => r.active.source == active.source))
if (active.state == 1 /* Pending */ && !this.running.some(r => r.active.source == active.source))
this.startQuery(active);
}
}
@ -1038,14 +1124,14 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
}
}
let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
if (current && current.state == 1 /* State.Pending */) {
if (current && current.state == 1 /* Pending */) {
if (query.done == null) {
// Explicitly failed. Should clear the pending status if it
// hasn't been re-set in the meantime.
let active = new ActiveSource(query.active.source, 0 /* State.Inactive */);
let active = new ActiveSource(query.active.source, 0 /* Inactive */);
for (let tr of query.updates)
active = active.update(tr, conf);
if (active.state != 1 /* State.Pending */)
if (active.state != 1 /* Pending */)
updated.push(active);
}
else {
@ -1059,21 +1145,24 @@ const completionPlugin = view.ViewPlugin.fromClass(class {
}
}, {
eventHandlers: {
blur() {
blur(event) {
let state = this.view.state.field(completionState, false);
if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur)
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur) {
let dialog = state.open && view.getTooltip(this.view, state.open.tooltip);
if (!dialog || !dialog.dom.contains(event.relatedTarget))
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
}
},
compositionstart() {
this.composing = 1 /* CompositionState.Started */;
this.composing = 1 /* Started */;
},
compositionend() {
if (this.composing == 3 /* CompositionState.ChangedAndMoved */) {
if (this.composing == 3 /* ChangedAndMoved */) {
// Safari fires compositionend events synchronously, possibly
// from inside an update, so dispatch asynchronously to avoid reentrancy
setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
}
this.composing = 0 /* CompositionState.None */;
this.composing = 0 /* None */;
}
}
});
@ -1092,13 +1181,21 @@ const baseTheme = view.EditorView.baseTheme({
listStyle: "none",
margin: 0,
padding: 0,
"& > li": {
overflowX: "hidden",
textOverflow: "ellipsis",
cursor: "pointer",
"& > li, & > completion-section": {
padding: "1px 3px",
lineHeight: 1.2
},
"& > li": {
overflowX: "hidden",
textOverflow: "ellipsis",
cursor: "pointer"
},
"& > completion-section": {
display: "list-item",
borderBottom: "1px solid silver",
paddingLeft: "0.5em",
opacity: 0.7
}
}
},
"&light .cm-tooltip-autocomplete ul li[aria-selected]": {
@ -1125,13 +1222,13 @@ const baseTheme = view.EditorView.baseTheme({
position: "absolute",
padding: "3px 9px",
width: "max-content",
maxWidth: `${400 /* Info.Width */}px`,
maxWidth: `${400 /* Width */}px`,
boxSizing: "border-box"
},
".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Info.Margin */}px` },
".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Info.Margin */}px` },
".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Margin */}px` },
".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Margin */}px` },
"&light .cm-snippetField": { backgroundColor: "#00000022" },
"&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
".cm-snippetFieldPosition": {
@ -1356,11 +1453,12 @@ interpreted as indicating a placeholder.
*/
function snippet(template) {
let snippet = Snippet.parse(template);
return (editor, _completion, from, to) => {
return (editor, completion, from, to) => {
let { text, ranges } = snippet.instantiate(editor.state, from);
let spec = {
changes: { from, to, insert: state.Text.of(text) },
scrollIntoView: true
scrollIntoView: true,
annotations: completion ? pickedCompletion.of(completion) : undefined
};
if (ranges.length)
spec.selection = fieldSelection(ranges, 0);
@ -1404,6 +1502,22 @@ const nextSnippetField = moveField(1);
Move to the previous snippet field, if available.
*/
const prevSnippetField = moveField(-1);
/**
Check if there is an active snippet with a next field for
`nextSnippetField` to move to.
*/
function hasNextSnippetField(state) {
let active = state.field(snippetState, false);
return !!(active && active.ranges.some(r => r.field == active.active + 1));
}
/**
Returns true if there is an active snippet and a previous field
for `prevSnippetField` to move to.
*/
function hasPrevSnippetField(state) {
let active = state.field(snippetState, false);
return !!(active && active.active > 0);
}
const defaultSnippetKeymap = [
{ key: "Tab", run: nextSnippetField, shift: prevSnippetField },
{ key: "Escape", run: clearSnippet }
@ -1467,7 +1581,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
if (!seen[m[0]] && pos + m.index != ignoreAt) {
result.push({ type: "text", label: m[0] });
seen[m[0]] = true;
if (result.length >= 2000 /* C.MaxList */)
if (result.length >= 2000 /* MaxList */)
return;
}
}
@ -1475,7 +1589,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
}
}
function collectWords(doc, cache, wordRE, to, ignoreAt) {
let big = doc.length >= 1000 /* C.MinCacheLen */;
let big = doc.length >= 1000 /* MinCacheLen */;
let cached = big && cache.get(doc);
if (cached)
return cached;
@ -1483,7 +1597,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
if (doc.children) {
let pos = 0;
for (let ch of doc.children) {
if (ch.length >= 1000 /* C.MinCacheLen */) {
if (ch.length >= 1000 /* MinCacheLen */) {
for (let c of collectWords(ch, cache, wordRE, to - pos, ignoreAt - pos)) {
if (!seen[c.label]) {
seen[c.label] = true;
@ -1500,7 +1614,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
else {
storeWords(doc, wordRE, result, seen, ignoreAt);
}
if (big && result.length < 2000 /* C.MaxList */)
if (big && result.length < 2000 /* MaxList */)
cache.set(doc, result);
return result;
}
@ -1516,7 +1630,7 @@ const completeAnyWord = context => {
if (!token && !context.explicit)
return null;
let from = token ? token.from : context.pos;
let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* C.Range */, from);
let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* Range */, from);
return { from, options, validFor: mapRE(re, s => "^" + s) };
};
@ -1531,9 +1645,6 @@ const closeBracketEffect = state.StateEffect.define({
return mapped == null ? undefined : mapped;
}
});
const skipBracketEffect = state.StateEffect.define({
map(value, mapping) { return mapping.mapPos(value); }
});
const closedBracket = new class extends state.RangeValue {
};
closedBracket.startSide = 1;
@ -1548,12 +1659,9 @@ const bracketState = state.StateField.define({
value = state.RangeSet.empty;
}
value = value.map(tr.changes);
for (let effect of tr.effects) {
for (let effect of tr.effects)
if (effect.is(closeBracketEffect))
value = value.update({ add: [closedBracket.range(effect.value, effect.value + 1)] });
else if (effect.is(skipBracketEffect))
value = value.update({ filter: from => from != effect.value });
}
return value;
}
});
@ -1681,15 +1789,15 @@ function handleOpen(state$1, open, close, closeBefore) {
});
}
function handleClose(state$1, _open, close) {
let dont = null, moved = state$1.selection.ranges.map(range => {
let dont = null, changes = state$1.changeByRange(range => {
if (range.empty && nextChar(state$1.doc, range.head) == close)
return state.EditorSelection.cursor(range.head + close.length);
return dont = range;
return { changes: { from: range.head, to: range.head + close.length, insert: close },
range: state.EditorSelection.cursor(range.head + close.length) };
return dont = { range };
});
return dont ? null : state$1.update({
selection: state.EditorSelection.create(moved, state$1.selection.mainIndex),
return dont ? null : state$1.update(changes, {
scrollIntoView: true,
effects: state$1.selection.ranges.map(({ from }) => skipBracketEffect.of(from))
userEvent: "input.type"
});
}
// Handles cases where the open and close token are the same, and
@ -1710,8 +1818,9 @@ function handleSame(state$1, token, allowTriple, config) {
}
else if (closedBracketAt(state$1, pos)) {
let isTriple = allowTriple && state$1.sliceDoc(pos, pos + token.length * 3) == token + token + token;
return { range: state.EditorSelection.cursor(pos + token.length * (isTriple ? 3 : 1)),
effects: skipBracketEffect.of(pos) };
let content = isTriple ? token + token + token : token;
return { changes: { from: pos, to: pos + content.length, insert: content },
range: state.EditorSelection.cursor(pos + content.length) };
}
}
else if (allowTriple && state$1.sliceDoc(pos - 2 * token.length, pos) == token + token &&
@ -1813,8 +1922,8 @@ returns `null`.
*/
function completionStatus(state) {
let cState = state.field(completionState, false);
return cState && cState.active.some(a => a.state == 1 /* State.Pending */) ? "pending"
: cState && cState.active.some(a => a.state != 0 /* State.Inactive */) ? "active" : null;
return cState && cState.active.some(a => a.state == 1 /* Pending */) ? "pending"
: cState && cState.active.some(a => a.state != 0 /* Inactive */) ? "active" : null;
}
const completionArrayCache = new WeakMap;
/**
@ -1868,6 +1977,8 @@ exports.completionKeymap = completionKeymap;
exports.completionStatus = completionStatus;
exports.currentCompletions = currentCompletions;
exports.deleteBracketPair = deleteBracketPair;
exports.hasNextSnippetField = hasNextSnippetField;
exports.hasPrevSnippetField = hasPrevSnippetField;
exports.ifIn = ifIn;
exports.ifNotIn = ifNotIn;
exports.insertBracket = insertBracket;

567
node_modules/@codemirror/autocomplete/dist/index.d.cts generated vendored Normal file
View file

@ -0,0 +1,567 @@
import * as _codemirror_state from '@codemirror/state';
import { EditorState, TransactionSpec, Transaction, StateCommand, Facet, Extension, StateEffect } from '@codemirror/state';
import { EditorView, Rect, KeyBinding, Command } from '@codemirror/view';
import * as _lezer_common from '@lezer/common';
/**
Objects type used to represent individual completions.
*/
interface Completion {
/**
The label to show in the completion picker. This is what input
is matched agains to determine whether a completion matches (and
how well it matches).
*/
label: string;
/**
An optional override for the completion's visible label. When
using this, matched characters will only be highlighted if you
provide a [`getMatch`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.getMatch)
function.
*/
displayLabel?: string;
/**
An optional short piece of information to show (with a different
style) after the label.
*/
detail?: string;
/**
Additional info to show when the completion is selected. Can be
a plain string or a function that'll render the DOM structure to
show when invoked.
*/
info?: string | ((completion: Completion) => CompletionInfo | Promise<CompletionInfo>);
/**
How to apply the completion. The default is to replace it with
its [label](https://codemirror.net/6/docs/ref/#autocomplete.Completion.label). When this holds a
string, the completion range is replaced by that string. When it
is a function, that function is called to perform the
completion. If it fires a transaction, it is responsible for
adding the [`pickedCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.pickedCompletion)
annotation to it.
*/
apply?: string | ((view: EditorView, completion: Completion, from: number, to: number) => void);
/**
The type of the completion. This is used to pick an icon to show
for the completion. Icons are styled with a CSS class created by
appending the type name to `"cm-completionIcon-"`. You can
define or restyle icons by defining these selectors. The base
library defines simple icons for `class`, `constant`, `enum`,
`function`, `interface`, `keyword`, `method`, `namespace`,
`property`, `text`, `type`, and `variable`.
Multiple types can be provided by separating them with spaces.
*/
type?: string;
/**
When given, should be a number from -99 to 99 that adjusts how
this completion is ranked compared to other completions that
match the input as well as this one. A negative number moves it
down the list, a positive number moves it up.
*/
boost?: number;
/**
Can be used to divide the completion list into sections.
Completions in a given section (matched by name) will be grouped
together, with a heading above them. Options without section
will appear above all sections. A string value is equivalent to
a `{name}` object.
*/
section?: string | CompletionSection;
}
/**
The type returned from
[`Completion.info`](https://codemirror.net/6/docs/ref/#autocomplete.Completion.info). May be a DOM
node, null to indicate there is no info, or an object with an
optional `destroy` method that cleans up the node.
*/
declare type CompletionInfo = Node | null | {
dom: Node;
destroy?(): void;
};
/**
Object used to describe a completion
[section](https://codemirror.net/6/docs/ref/#autocomplete.Completion.section). It is recommended to
create a shared object used by all the completions in a given
section.
*/
interface CompletionSection {
/**
The name of the section. If no `render` method is present, this
will be displayed above the options.
*/
name: string;
/**
An optional function that renders the section header. Since the
headers are shown inside a list, you should make sure the
resulting element has a `display: list-item` style.
*/
header?: (section: CompletionSection) => HTMLElement;
/**
By default, sections are ordered alphabetically by name. To
specify an explicit order, `rank` can be used. Sections with a
lower rank will be shown above sections with a higher rank.
*/
rank?: number;
}
/**
An instance of this is passed to completion source functions.
*/
declare class CompletionContext {
/**
The editor state that the completion happens in.
*/
readonly state: EditorState;
/**
The position at which the completion is happening.
*/
readonly pos: number;
/**
Indicates whether completion was activated explicitly, or
implicitly by typing. The usual way to respond to this is to
only return completions when either there is part of a
completable entity before the cursor, or `explicit` is true.
*/
readonly explicit: boolean;
/**
Create a new completion context. (Mostly useful for testing
completion sourcesin the editor, the extension will create
these for you.)
*/
constructor(
/**
The editor state that the completion happens in.
*/
state: EditorState,
/**
The position at which the completion is happening.
*/
pos: number,
/**
Indicates whether completion was activated explicitly, or
implicitly by typing. The usual way to respond to this is to
only return completions when either there is part of a
completable entity before the cursor, or `explicit` is true.
*/
explicit: boolean);
/**
Get the extent, content, and (if there is a token) type of the
token before `this.pos`.
*/
tokenBefore(types: readonly string[]): {
from: number;
to: number;
text: string;
type: _lezer_common.NodeType;
} | null;
/**
Get the match of the given expression directly before the
cursor.
*/
matchBefore(expr: RegExp): {
from: number;
to: number;
text: string;
} | null;
/**
Yields true when the query has been aborted. Can be useful in
asynchronous queries to avoid doing work that will be ignored.
*/
get aborted(): boolean;
/**
Allows you to register abort handlers, which will be called when
the query is
[aborted](https://codemirror.net/6/docs/ref/#autocomplete.CompletionContext.aborted).
*/
addEventListener(type: "abort", listener: () => void): void;
}
/**
Given a a fixed array of options, return an autocompleter that
completes them.
*/
declare function completeFromList(list: readonly (string | Completion)[]): CompletionSource;
/**
Wrap the given completion source so that it will only fire when the
cursor is in a syntax node with one of the given names.
*/
declare function ifIn(nodes: readonly string[], source: CompletionSource): CompletionSource;
/**
Wrap the given completion source so that it will not fire when the
cursor is in a syntax node with one of the given names.
*/
declare function ifNotIn(nodes: readonly string[], source: CompletionSource): CompletionSource;
/**
The function signature for a completion source. Such a function
may return its [result](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult)
synchronously or as a promise. Returning null indicates no
completions are available.
*/
declare type CompletionSource = (context: CompletionContext) => CompletionResult | null | Promise<CompletionResult | null>;
/**
Interface for objects returned by completion sources.
*/
interface CompletionResult {
/**
The start of the range that is being completed.
*/
from: number;
/**
The end of the range that is being completed. Defaults to the
main cursor position.
*/
to?: number;
/**
The completions returned. These don't have to be compared with
the input by the sourcethe autocompletion system will do its
own matching (against the text between `from` and `to`) and
sorting.
*/
options: readonly Completion[];
/**
When given, further typing or deletion that causes the part of
the document between ([mapped](https://codemirror.net/6/docs/ref/#state.ChangeDesc.mapPos)) `from`
and `to` to match this regular expression or predicate function
will not query the completion source again, but continue with
this list of options. This can help a lot with responsiveness,
since it allows the completion list to be updated synchronously.
*/
validFor?: RegExp | ((text: string, from: number, to: number, state: EditorState) => boolean);
/**
By default, the library filters and scores completions. Set
`filter` to `false` to disable this, and cause your completions
to all be included, in the order they were given. When there are
other sources, unfiltered completions appear at the top of the
list of completions. `validFor` must not be given when `filter`
is `false`, because it only works when filtering.
*/
filter?: boolean;
/**
When [`filter`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.filter) is set to
`false` or a completion has a
[`displayLabel`](https://codemirror.net/6/docs/ref/#autocomplete.Completion.displayLabel), this
may be provided to compute the ranges on the label that match
the input. Should return an array of numbers where each pair of
adjacent numbers provide the start and end of a range. The
second argument, the match found by the library, is only passed
when `filter` isn't `false`.
*/
getMatch?: (completion: Completion, matched?: readonly number[]) => readonly number[];
/**
Synchronously update the completion result after typing or
deletion. If given, this should not do any expensive work, since
it will be called during editor state updates. The function
should make sure (similar to
[`validFor`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.validFor)) that the
completion still applies in the new state.
*/
update?: (current: CompletionResult, from: number, to: number, context: CompletionContext) => CompletionResult | null;
}
/**
This annotation is added to transactions that are produced by
picking a completion.
*/
declare const pickedCompletion: _codemirror_state.AnnotationType<Completion>;
/**
Helper function that returns a transaction spec which inserts a
completion's text in the main selection range, and any other
selection range that has the same text in front of it.
*/
declare function insertCompletionText(state: EditorState, text: string, from: number, to: number): TransactionSpec;
interface CompletionConfig {
/**
When enabled (defaults to true), autocompletion will start
whenever the user types something that can be completed.
*/
activateOnTyping?: boolean;
/**
By default, when completion opens, the first option is selected
and can be confirmed with
[`acceptCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.acceptCompletion). When this
is set to false, the completion widget starts with no completion
selected, and the user has to explicitly move to a completion
before you can confirm one.
*/
selectOnOpen?: boolean;
/**
Override the completion sources used. By default, they will be
taken from the `"autocomplete"` [language
data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) (which should hold
[completion sources](https://codemirror.net/6/docs/ref/#autocomplete.CompletionSource) or arrays
of [completions](https://codemirror.net/6/docs/ref/#autocomplete.Completion)).
*/
override?: readonly CompletionSource[] | null;
/**
Determines whether the completion tooltip is closed when the
editor loses focus. Defaults to true.
*/
closeOnBlur?: boolean;
/**
The maximum number of options to render to the DOM.
*/
maxRenderedOptions?: number;
/**
Set this to false to disable the [default completion
keymap](https://codemirror.net/6/docs/ref/#autocomplete.completionKeymap). (This requires you to
add bindings to control completion yourself. The bindings should
probably have a higher precedence than other bindings for the
same keys.)
*/
defaultKeymap?: boolean;
/**
By default, completions are shown below the cursor when there is
space. Setting this to true will make the extension put the
completions above the cursor when possible.
*/
aboveCursor?: boolean;
/**
When given, this may return an additional CSS class to add to
the completion dialog element.
*/
tooltipClass?: (state: EditorState) => string;
/**
This can be used to add additional CSS classes to completion
options.
*/
optionClass?: (completion: Completion) => string;
/**
By default, the library will render icons based on the
completion's [type](https://codemirror.net/6/docs/ref/#autocomplete.Completion.type) in front of
each option. Set this to false to turn that off.
*/
icons?: boolean;
/**
This option can be used to inject additional content into
options. The `render` function will be called for each visible
completion, and should produce a DOM node to show. `position`
determines where in the DOM the result appears, relative to
other added widgets and the standard content. The default icons
have position 20, the label position 50, and the detail position
80.
*/
addToOptions?: {
render: (completion: Completion, state: EditorState) => Node | null;
position: number;
}[];
/**
By default, [info](https://codemirror.net/6/docs/ref/#autocomplet.Completion.info) tooltips are
placed to the side of the selected. This option can be used to
override that. It will be given rectangles for the list of
completions, the selected option, the info element, and the
availble [tooltip space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace),
and should return style and/or class strings for the info
element.
*/
positionInfo?: (view: EditorView, list: Rect, option: Rect, info: Rect, space: Rect) => {
style?: string;
class?: string;
};
/**
The comparison function to use when sorting completions with the same
match score. Defaults to using
[`localeCompare`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
*/
compareCompletions?: (a: Completion, b: Completion) => number;
/**
By default, commands relating to an open completion only take
effect 75 milliseconds after the completion opened, so that key
presses made before the user is aware of the tooltip don't go to
the tooltip. This option can be used to configure that delay.
*/
interactionDelay?: number;
}
/**
Convert a snippet template to a function that can
[apply](https://codemirror.net/6/docs/ref/#autocomplete.Completion.apply) it. Snippets are written
using syntax like this:
"for (let ${index} = 0; ${index} < ${end}; ${index}++) {\n\t${}\n}"
Each `${}` placeholder (you may also use `#{}`) indicates a field
that the user can fill in. Its name, if any, will be the default
content for the field.
When the snippet is activated by calling the returned function,
the code is inserted at the given position. Newlines in the
template are indented by the indentation of the start line, plus
one [indent unit](https://codemirror.net/6/docs/ref/#language.indentUnit) per tab character after
the newline.
On activation, (all instances of) the first field are selected.
The user can move between fields with Tab and Shift-Tab as long as
the fields are active. Moving to the last field or moving the
cursor out of the current field deactivates the fields.
The order of fields defaults to textual order, but you can add
numbers to placeholders (`${1}` or `${1:defaultText}`) to provide
a custom order.
To include a literal `{` or `}` in your template, put a backslash
in front of it. This will be removed and the brace will not be
interpreted as indicating a placeholder.
*/
declare function snippet(template: string): (editor: {
state: EditorState;
dispatch: (tr: Transaction) => void;
}, completion: Completion | null, from: number, to: number) => void;
/**
A command that clears the active snippet, if any.
*/
declare const clearSnippet: StateCommand;
/**
Move to the next snippet field, if available.
*/
declare const nextSnippetField: StateCommand;
/**
Move to the previous snippet field, if available.
*/
declare const prevSnippetField: StateCommand;
/**
Check if there is an active snippet with a next field for
`nextSnippetField` to move to.
*/
declare function hasNextSnippetField(state: EditorState): boolean;
/**
Returns true if there is an active snippet and a previous field
for `prevSnippetField` to move to.
*/
declare function hasPrevSnippetField(state: EditorState): boolean;
/**
A facet that can be used to configure the key bindings used by
snippets. The default binds Tab to
[`nextSnippetField`](https://codemirror.net/6/docs/ref/#autocomplete.nextSnippetField), Shift-Tab to
[`prevSnippetField`](https://codemirror.net/6/docs/ref/#autocomplete.prevSnippetField), and Escape
to [`clearSnippet`](https://codemirror.net/6/docs/ref/#autocomplete.clearSnippet).
*/
declare const snippetKeymap: Facet<readonly KeyBinding[], readonly KeyBinding[]>;
/**
Create a completion from a snippet. Returns an object with the
properties from `completion`, plus an `apply` function that
applies the snippet.
*/
declare function snippetCompletion(template: string, completion: Completion): Completion;
/**
Returns a command that moves the completion selection forward or
backward by the given amount.
*/
declare function moveCompletionSelection(forward: boolean, by?: "option" | "page"): Command;
/**
Accept the current completion.
*/
declare const acceptCompletion: Command;
/**
Explicitly start autocompletion.
*/
declare const startCompletion: Command;
/**
Close the currently active completion.
*/
declare const closeCompletion: Command;
/**
A completion source that will scan the document for words (using a
[character categorizer](https://codemirror.net/6/docs/ref/#state.EditorState.charCategorizer)), and
return those as completions.
*/
declare const completeAnyWord: CompletionSource;
/**
Configures bracket closing behavior for a syntax (via
[language data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt)) using the `"closeBrackets"`
identifier.
*/
interface CloseBracketConfig {
/**
The opening brackets to close. Defaults to `["(", "[", "{", "'",
'"']`. Brackets may be single characters or a triple of quotes
(as in `"''''"`).
*/
brackets?: string[];
/**
Characters in front of which newly opened brackets are
automatically closed. Closing always happens in front of
whitespace. Defaults to `")]}:;>"`.
*/
before?: string;
/**
When determining whether a given node may be a string, recognize
these prefixes before the opening quote.
*/
stringPrefixes?: string[];
}
/**
Extension to enable bracket-closing behavior. When a closeable
bracket is typed, its closing bracket is immediately inserted
after the cursor. When closing a bracket directly in front of a
closing bracket inserted by the extension, the cursor moves over
that bracket.
*/
declare function closeBrackets(): Extension;
/**
Command that implements deleting a pair of matching brackets when
the cursor is between them.
*/
declare const deleteBracketPair: StateCommand;
/**
Close-brackets related key bindings. Binds Backspace to
[`deleteBracketPair`](https://codemirror.net/6/docs/ref/#autocomplete.deleteBracketPair).
*/
declare const closeBracketsKeymap: readonly KeyBinding[];
/**
Implements the extension's behavior on text insertion. If the
given string counts as a bracket in the language around the
selection, and replacing the selection with it requires custom
behavior (inserting a closing version or skipping past a
previously-closed bracket), this function returns a transaction
representing that custom behavior. (You only need this if you want
to programmatically insert bracketsthe
[`closeBrackets`](https://codemirror.net/6/docs/ref/#autocomplete.closeBrackets) extension will
take care of running this for user input.)
*/
declare function insertBracket(state: EditorState, bracket: string): Transaction | null;
/**
Returns an extension that enables autocompletion.
*/
declare function autocompletion(config?: CompletionConfig): Extension;
/**
Basic keybindings for autocompletion.
- Ctrl-Space: [`startCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.startCompletion)
- Escape: [`closeCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.closeCompletion)
- ArrowDown: [`moveCompletionSelection`](https://codemirror.net/6/docs/ref/#autocomplete.moveCompletionSelection)`(true)`
- ArrowUp: [`moveCompletionSelection`](https://codemirror.net/6/docs/ref/#autocomplete.moveCompletionSelection)`(false)`
- PageDown: [`moveCompletionSelection`](https://codemirror.net/6/docs/ref/#autocomplete.moveCompletionSelection)`(true, "page")`
- PageDown: [`moveCompletionSelection`](https://codemirror.net/6/docs/ref/#autocomplete.moveCompletionSelection)`(true, "page")`
- Enter: [`acceptCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.acceptCompletion)
*/
declare const completionKeymap: readonly KeyBinding[];
/**
Get the current completion status. When completions are available,
this will return `"active"`. When completions are pending (in the
process of being queried), this returns `"pending"`. Otherwise, it
returns `null`.
*/
declare function completionStatus(state: EditorState): null | "active" | "pending";
/**
Returns the available completions as an array.
*/
declare function currentCompletions(state: EditorState): readonly Completion[];
/**
Return the currently selected completion, if any.
*/
declare function selectedCompletion(state: EditorState): Completion | null;
/**
Returns the currently selected position in the active completion
list, or null if no completions are active.
*/
declare function selectedCompletionIndex(state: EditorState): number | null;
/**
Create an effect that can be attached to a transaction to change
the currently selected completion.
*/
declare function setSelectedCompletion(index: number): StateEffect<unknown>;
export { CloseBracketConfig, Completion, CompletionContext, CompletionInfo, CompletionResult, CompletionSection, CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeBrackets, closeBracketsKeymap, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, deleteBracketPair, hasNextSnippetField, hasPrevSnippetField, ifIn, ifNotIn, insertBracket, insertCompletionText, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, selectedCompletionIndex, setSelectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };

View file

@ -1,98 +1,8 @@
import * as _codemirror_state from '@codemirror/state';
import { EditorState, TransactionSpec, Transaction, StateCommand, Facet, Extension, StateEffect } from '@codemirror/state';
import { EditorView, KeyBinding, Command } from '@codemirror/view';
import { EditorView, Rect, KeyBinding, Command } from '@codemirror/view';
import * as _lezer_common from '@lezer/common';
interface CompletionConfig {
/**
When enabled (defaults to true), autocompletion will start
whenever the user types something that can be completed.
*/
activateOnTyping?: boolean;
/**
By default, when completion opens, the first option is selected
and can be confirmed with
[`acceptCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.acceptCompletion). When this
is set to false, the completion widget starts with no completion
selected, and the user has to explicitly move to a completion
before you can confirm one.
*/
selectOnOpen?: boolean;
/**
Override the completion sources used. By default, they will be
taken from the `"autocomplete"` [language
data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) (which should hold
[completion sources](https://codemirror.net/6/docs/ref/#autocomplete.CompletionSource) or arrays
of [completions](https://codemirror.net/6/docs/ref/#autocomplete.Completion)).
*/
override?: readonly CompletionSource[] | null;
/**
Determines whether the completion tooltip is closed when the
editor loses focus. Defaults to true.
*/
closeOnBlur?: boolean;
/**
The maximum number of options to render to the DOM.
*/
maxRenderedOptions?: number;
/**
Set this to false to disable the [default completion
keymap](https://codemirror.net/6/docs/ref/#autocomplete.completionKeymap). (This requires you to
add bindings to control completion yourself. The bindings should
probably have a higher precedence than other bindings for the
same keys.)
*/
defaultKeymap?: boolean;
/**
By default, completions are shown below the cursor when there is
space. Setting this to true will make the extension put the
completions above the cursor when possible.
*/
aboveCursor?: boolean;
/**
When given, this may return an additional CSS class to add to
the completion dialog element.
*/
tooltipClass?: (state: EditorState) => string;
/**
This can be used to add additional CSS classes to completion
options.
*/
optionClass?: (completion: Completion) => string;
/**
By default, the library will render icons based on the
completion's [type](https://codemirror.net/6/docs/ref/#autocomplete.Completion.type) in front of
each option. Set this to false to turn that off.
*/
icons?: boolean;
/**
This option can be used to inject additional content into
options. The `render` function will be called for each visible
completion, and should produce a DOM node to show. `position`
determines where in the DOM the result appears, relative to
other added widgets and the standard content. The default icons
have position 20, the label position 50, and the detail position
80.
*/
addToOptions?: {
render: (completion: Completion, state: EditorState) => Node | null;
position: number;
}[];
/**
The comparison function to use when sorting completions with the same
match score. Defaults to using
[`localeCompare`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
*/
compareCompletions?: (a: Completion, b: Completion) => number;
/**
By default, commands relating to an open completion only take
effect 75 milliseconds after the completion opened, so that key
presses made before the user is aware of the tooltip don't go to
the tooltip. This option can be used to configure that delay.
*/
interactionDelay?: number;
}
/**
Objects type used to represent individual completions.
*/
@ -104,6 +14,13 @@ interface Completion {
*/
label: string;
/**
An optional override for the completion's visible label. When
using this, matched characters will only be highlighted if you
provide a [`getMatch`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.getMatch)
function.
*/
displayLabel?: string;
/**
An optional short piece of information to show (with a different
style) after the label.
*/
@ -113,7 +30,7 @@ interface Completion {
a plain string or a function that'll render the DOM structure to
show when invoked.
*/
info?: string | ((completion: Completion) => (Node | null | Promise<Node | null>));
info?: string | ((completion: Completion) => CompletionInfo | Promise<CompletionInfo>);
/**
How to apply the completion. The default is to replace it with
its [label](https://codemirror.net/6/docs/ref/#autocomplete.Completion.label). When this holds a
@ -143,6 +60,49 @@ interface Completion {
down the list, a positive number moves it up.
*/
boost?: number;
/**
Can be used to divide the completion list into sections.
Completions in a given section (matched by name) will be grouped
together, with a heading above them. Options without section
will appear above all sections. A string value is equivalent to
a `{name}` object.
*/
section?: string | CompletionSection;
}
/**
The type returned from
[`Completion.info`](https://codemirror.net/6/docs/ref/#autocomplete.Completion.info). May be a DOM
node, null to indicate there is no info, or an object with an
optional `destroy` method that cleans up the node.
*/
declare type CompletionInfo = Node | null | {
dom: Node;
destroy?(): void;
};
/**
Object used to describe a completion
[section](https://codemirror.net/6/docs/ref/#autocomplete.Completion.section). It is recommended to
create a shared object used by all the completions in a given
section.
*/
interface CompletionSection {
/**
The name of the section. If no `render` method is present, this
will be displayed above the options.
*/
name: string;
/**
An optional function that renders the section header. Since the
headers are shown inside a list, you should make sure the
resulting element has a `display: list-item` style.
*/
header?: (section: CompletionSection) => HTMLElement;
/**
By default, sections are ordered alphabetically by name. To
specify an explicit order, `rank` can be used. Sections with a
lower rank will be shown above sections with a higher rank.
*/
rank?: number;
}
/**
An instance of this is passed to completion source functions.
@ -277,12 +237,15 @@ interface CompletionResult {
filter?: boolean;
/**
When [`filter`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.filter) is set to
`false`, this may be provided to compute the ranges on the label
that match the input. Should return an array of numbers where
each pair of adjacent numbers provide the start and end of a
range.
`false` or a completion has a
[`displayLabel`](https://codemirror.net/6/docs/ref/#autocomplete.Completion.displayLabel), this
may be provided to compute the ranges on the label that match
the input. Should return an array of numbers where each pair of
adjacent numbers provide the start and end of a range. The
second argument, the match found by the library, is only passed
when `filter` isn't `false`.
*/
getMatch?: (completion: Completion) => readonly number[];
getMatch?: (completion: Completion, matched?: readonly number[]) => readonly number[];
/**
Synchronously update the completion result after typing or
deletion. If given, this should not do any expensive work, since
@ -305,6 +268,109 @@ selection range that has the same text in front of it.
*/
declare function insertCompletionText(state: EditorState, text: string, from: number, to: number): TransactionSpec;
interface CompletionConfig {
/**
When enabled (defaults to true), autocompletion will start
whenever the user types something that can be completed.
*/
activateOnTyping?: boolean;
/**
By default, when completion opens, the first option is selected
and can be confirmed with
[`acceptCompletion`](https://codemirror.net/6/docs/ref/#autocomplete.acceptCompletion). When this
is set to false, the completion widget starts with no completion
selected, and the user has to explicitly move to a completion
before you can confirm one.
*/
selectOnOpen?: boolean;
/**
Override the completion sources used. By default, they will be
taken from the `"autocomplete"` [language
data](https://codemirror.net/6/docs/ref/#state.EditorState.languageDataAt) (which should hold
[completion sources](https://codemirror.net/6/docs/ref/#autocomplete.CompletionSource) or arrays
of [completions](https://codemirror.net/6/docs/ref/#autocomplete.Completion)).
*/
override?: readonly CompletionSource[] | null;
/**
Determines whether the completion tooltip is closed when the
editor loses focus. Defaults to true.
*/
closeOnBlur?: boolean;
/**
The maximum number of options to render to the DOM.
*/
maxRenderedOptions?: number;
/**
Set this to false to disable the [default completion
keymap](https://codemirror.net/6/docs/ref/#autocomplete.completionKeymap). (This requires you to
add bindings to control completion yourself. The bindings should
probably have a higher precedence than other bindings for the
same keys.)
*/
defaultKeymap?: boolean;
/**
By default, completions are shown below the cursor when there is
space. Setting this to true will make the extension put the
completions above the cursor when possible.
*/
aboveCursor?: boolean;
/**
When given, this may return an additional CSS class to add to
the completion dialog element.
*/
tooltipClass?: (state: EditorState) => string;
/**
This can be used to add additional CSS classes to completion
options.
*/
optionClass?: (completion: Completion) => string;
/**
By default, the library will render icons based on the
completion's [type](https://codemirror.net/6/docs/ref/#autocomplete.Completion.type) in front of
each option. Set this to false to turn that off.
*/
icons?: boolean;
/**
This option can be used to inject additional content into
options. The `render` function will be called for each visible
completion, and should produce a DOM node to show. `position`
determines where in the DOM the result appears, relative to
other added widgets and the standard content. The default icons
have position 20, the label position 50, and the detail position
80.
*/
addToOptions?: {
render: (completion: Completion, state: EditorState) => Node | null;
position: number;
}[];
/**
By default, [info](https://codemirror.net/6/docs/ref/#autocomplet.Completion.info) tooltips are
placed to the side of the selected. This option can be used to
override that. It will be given rectangles for the list of
completions, the selected option, the info element, and the
availble [tooltip space](https://codemirror.net/6/docs/ref/#view.tooltips^config.tooltipSpace),
and should return style and/or class strings for the info
element.
*/
positionInfo?: (view: EditorView, list: Rect, option: Rect, info: Rect, space: Rect) => {
style?: string;
class?: string;
};
/**
The comparison function to use when sorting completions with the same
match score. Defaults to using
[`localeCompare`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/localeCompare).
*/
compareCompletions?: (a: Completion, b: Completion) => number;
/**
By default, commands relating to an open completion only take
effect 75 milliseconds after the completion opened, so that key
presses made before the user is aware of the tooltip don't go to
the tooltip. This option can be used to configure that delay.
*/
interactionDelay?: number;
}
/**
Convert a snippet template to a function that can
[apply](https://codemirror.net/6/docs/ref/#autocomplete.Completion.apply) it. Snippets are written
@ -338,7 +404,7 @@ interpreted as indicating a placeholder.
declare function snippet(template: string): (editor: {
state: EditorState;
dispatch: (tr: Transaction) => void;
}, _completion: Completion, from: number, to: number) => void;
}, completion: Completion | null, from: number, to: number) => void;
/**
A command that clears the active snippet, if any.
*/
@ -352,6 +418,16 @@ Move to the previous snippet field, if available.
*/
declare const prevSnippetField: StateCommand;
/**
Check if there is an active snippet with a next field for
`nextSnippetField` to move to.
*/
declare function hasNextSnippetField(state: EditorState): boolean;
/**
Returns true if there is an active snippet and a previous field
for `prevSnippetField` to move to.
*/
declare function hasPrevSnippetField(state: EditorState): boolean;
/**
A facet that can be used to configure the key bindings used by
snippets. The default binds Tab to
[`nextSnippetField`](https://codemirror.net/6/docs/ref/#autocomplete.nextSnippetField), Shift-Tab to
@ -488,4 +564,4 @@ the currently selected completion.
*/
declare function setSelectedCompletion(index: number): StateEffect<unknown>;
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 };
export { CloseBracketConfig, Completion, CompletionContext, CompletionInfo, CompletionResult, CompletionSection, CompletionSource, acceptCompletion, autocompletion, clearSnippet, closeBrackets, closeBracketsKeymap, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, deleteBracketPair, hasNextSnippetField, hasPrevSnippetField, ifIn, ifNotIn, insertBracket, insertCompletionText, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, selectedCompletionIndex, setSelectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };

View file

@ -1,5 +1,5 @@
import { Annotation, EditorSelection, codePointAt, codePointSize, fromCodePoint, Facet, combineConfig, StateEffect, StateField, Prec, Text, MapMode, RangeValue, RangeSet, CharCategory } from '@codemirror/state';
import { logException, Direction, showTooltip, EditorView, ViewPlugin, getTooltip, Decoration, WidgetType, keymap } from '@codemirror/view';
import { Annotation, StateEffect, EditorSelection, codePointAt, codePointSize, fromCodePoint, Facet, combineConfig, StateField, Prec, Text, MapMode, RangeValue, RangeSet, CharCategory } from '@codemirror/state';
import { Direction, logException, showTooltip, EditorView, ViewPlugin, getTooltip, Decoration, WidgetType, keymap } from '@codemirror/view';
import { syntaxTree, indentUnit } from '@codemirror/language';
/**
@ -108,9 +108,12 @@ cursor is in a syntax node with one of the given names.
*/
function ifIn(nodes, source) {
return (context) => {
for (let pos = syntaxTree(context.state).resolveInner(context.pos, -1); pos; pos = pos.parent)
for (let pos = syntaxTree(context.state).resolveInner(context.pos, -1); pos; pos = pos.parent) {
if (nodes.indexOf(pos.name) > -1)
return source(context);
if (pos.type.isTop)
break;
}
return null;
};
}
@ -120,20 +123,24 @@ cursor is in a syntax node with one of the given names.
*/
function ifNotIn(nodes, source) {
return (context) => {
for (let pos = syntaxTree(context.state).resolveInner(context.pos, -1); pos; pos = pos.parent)
for (let pos = syntaxTree(context.state).resolveInner(context.pos, -1); pos; pos = pos.parent) {
if (nodes.indexOf(pos.name) > -1)
return null;
if (pos.type.isTop)
break;
}
return source(context);
};
}
class Option {
constructor(completion, source, match) {
constructor(completion, source, match, score) {
this.completion = completion;
this.source = source;
this.match = match;
this.score = score;
}
}
function cur(state) { return state.selection.main.head; }
function cur(state) { return state.selection.main.from; }
// Make sure the given regexp has a $ at its end and, if `start` is
// true, a ^ at its start.
function ensureAnchor(expr, start) {
@ -155,30 +162,17 @@ completion's text in the main selection range, and any other
selection range that has the same text in front of it.
*/
function insertCompletionText(state, text, from, to) {
let { main } = state.selection, fromOff = from - main.from, toOff = to - main.from;
return Object.assign(Object.assign({}, state.changeByRange(range => {
if (range == state.selection.main)
return {
changes: { from: from, to: to, insert: text },
range: EditorSelection.cursor(from + text.length)
};
let len = to - from;
if (!range.empty ||
len && state.sliceDoc(range.from - len, range.from) != state.sliceDoc(from, to))
if (range != main && from != to &&
state.sliceDoc(range.from + fromOff, range.from + toOff) != state.sliceDoc(from, to))
return { range };
return {
changes: { from: range.from - len, to: range.from, insert: text },
range: EditorSelection.cursor(range.from - len + text.length)
changes: { from: range.from + fromOff, to: to == main.from ? range.to : range.from + toOff, insert: text },
range: EditorSelection.cursor(range.from + fromOff + text.length)
};
})), { userEvent: "input.complete" });
}
function applyCompletion(view, option) {
const apply = option.completion.apply || option.completion.label;
let result = option.source;
if (typeof apply == "string")
view.dispatch(Object.assign(Object.assign({}, insertCompletionText(view.state, apply, result.from, result.to)), { annotations: pickedCompletion.of(option.completion) }));
else
apply(view, option.completion, result.from, result.to);
}
const SourceCache = /*@__PURE__*/new WeakMap();
function asSource(source) {
if (!Array.isArray(source))
@ -188,6 +182,8 @@ function asSource(source) {
SourceCache.set(source, known = completeFromList(source));
return known;
}
const startCompletionEffect = /*@__PURE__*/StateEffect.define();
const closeCompletionEffect = /*@__PURE__*/StateEffect.define();
// A pattern matcher for fuzzy completion matching. Create an instance
// once for a pattern, and then use that to match any number of
@ -202,6 +198,8 @@ class FuzzyMatcher {
this.any = [];
this.precise = [];
this.byWord = [];
this.score = 0;
this.matched = [];
for (let p = 0; p < pattern.length;) {
let char = codePointAt(pattern, p), size = codePointSize(char);
this.chars.push(char);
@ -211,29 +209,39 @@ class FuzzyMatcher {
}
this.astral = pattern.length != this.chars.length;
}
ret(score, matched) {
this.score = score;
this.matched = matched;
return true;
}
// Matches a given word (completion) against the pattern (input).
// Will return null for no match, and otherwise an array that starts
// with the match score, followed by any number of `from, to` pairs
// indicating the matched parts of `word`.
// Will return a boolean indicating whether there was a match and,
// on success, set `this.score` to the score, `this.matched` to an
// array of `from, to` pairs indicating the matched parts of `word`.
//
// The score is a number that is more negative the worse the match
// is. See `Penalty` above.
match(word) {
if (this.pattern.length == 0)
return [0];
return this.ret(-100 /* NotFull */, []);
if (word.length < this.pattern.length)
return null;
return false;
let { chars, folded, any, precise, byWord } = this;
// For single-character queries, only match when they occur right
// at the start
if (chars.length == 1) {
let first = codePointAt(word, 0);
return first == chars[0] ? [0, 0, codePointSize(first)]
: first == folded[0] ? [-200 /* Penalty.CaseFold */, 0, codePointSize(first)] : null;
let first = codePointAt(word, 0), firstSize = codePointSize(first);
let score = firstSize == word.length ? 0 : -100 /* NotFull */;
if (first == chars[0]) ;
else if (first == folded[0])
score += -200 /* CaseFold */;
else
return false;
return this.ret(score, [0, firstSize]);
}
let direct = word.indexOf(this.pattern);
if (direct == 0)
return [0, 0, this.pattern.length];
return this.ret(word.length == this.pattern.length ? 0 : -100 /* NotFull */, [0, this.pattern.length]);
let len = chars.length, anyTo = 0;
if (direct < 0) {
for (let i = 0, e = Math.min(word.length, 200); i < e && anyTo < len;) {
@ -244,7 +252,7 @@ class FuzzyMatcher {
}
// No match, exit immediately
if (anyTo < len)
return null;
return false;
}
// This tracks the extent of the precise (non-folded, not
// necessarily adjacent) match
@ -257,7 +265,7 @@ class FuzzyMatcher {
let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
let hasLower = /[a-z]/.test(word), wordAdjacent = true;
// Go over the option's text, scanning for the various kinds of matches
for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* Tp.NonWord */; i < e && byWordTo < len;) {
for (let i = 0, e = Math.min(word.length, 200), prevType = 0 /* NonWord */; i < e && byWordTo < len;) {
let next = codePointAt(word, i);
if (direct < 0) {
if (preciseTo < len && next == chars[preciseTo])
@ -275,9 +283,9 @@ class FuzzyMatcher {
}
}
let ch, type = next < 0xff
? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Tp.Lower */ : next >= 65 && next <= 90 ? 1 /* Tp.Upper */ : 0 /* Tp.NonWord */)
: ((ch = fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Tp.Upper */ : ch != ch.toUpperCase() ? 2 /* Tp.Lower */ : 0 /* Tp.NonWord */);
if (!i || type == 1 /* Tp.Upper */ && hasLower || prevType == 0 /* Tp.NonWord */ && type != 0 /* Tp.NonWord */) {
? (next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 /* Lower */ : next >= 65 && next <= 90 ? 1 /* Upper */ : 0 /* NonWord */)
: ((ch = fromCodePoint(next)) != ch.toLowerCase() ? 1 /* Upper */ : ch != ch.toUpperCase() ? 2 /* Lower */ : 0 /* NonWord */);
if (!i || type == 1 /* Upper */ && hasLower || prevType == 0 /* NonWord */ && type != 0 /* NonWord */) {
if (chars[byWordTo] == next || (folded[byWordTo] == next && (byWordFolded = true)))
byWord[byWordTo++] = i;
else if (byWord.length)
@ -287,30 +295,31 @@ class FuzzyMatcher {
i += codePointSize(next);
}
if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0), byWord, word);
return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0), byWord, word);
if (adjacentTo == len && adjacentStart == 0)
return [-200 /* Penalty.CaseFold */ - word.length, 0, adjacentEnd];
return this.ret(-200 /* CaseFold */ - word.length + (adjacentEnd == word.length ? 0 : -100 /* NotFull */), [0, adjacentEnd]);
if (direct > -1)
return [-700 /* Penalty.NotStart */ - word.length, direct, direct + this.pattern.length];
return this.ret(-700 /* NotStart */ - word.length, [direct, direct + this.pattern.length]);
if (adjacentTo == len)
return [-200 /* Penalty.CaseFold */ + -700 /* Penalty.NotStart */ - word.length, adjacentStart, adjacentEnd];
return this.ret(-200 /* CaseFold */ + -700 /* NotStart */ - word.length, [adjacentStart, adjacentEnd]);
if (byWordTo == len)
return this.result(-100 /* Penalty.ByWord */ + (byWordFolded ? -200 /* Penalty.CaseFold */ : 0) + -700 /* Penalty.NotStart */ +
(wordAdjacent ? 0 : -1100 /* Penalty.Gap */), byWord, word);
return chars.length == 2 ? null : this.result((any[0] ? -700 /* Penalty.NotStart */ : 0) + -200 /* Penalty.CaseFold */ + -1100 /* Penalty.Gap */, any, word);
return this.result(-100 /* ByWord */ + (byWordFolded ? -200 /* CaseFold */ : 0) + -700 /* NotStart */ +
(wordAdjacent ? 0 : -1100 /* Gap */), byWord, word);
return chars.length == 2 ? false
: this.result((any[0] ? -700 /* NotStart */ : 0) + -200 /* CaseFold */ + -1100 /* Gap */, any, word);
}
result(score, positions, word) {
let result = [score - word.length], i = 1;
let result = [], i = 0;
for (let pos of positions) {
let to = pos + (this.astral ? codePointSize(codePointAt(word, pos)) : 1);
if (i > 1 && result[i - 1] == pos)
if (i && result[i - 1] == pos)
result[i - 1] = to;
else {
result[i++] = pos;
result[i++] = to;
}
}
return result;
return this.ret(score - word.length, result);
}
}
@ -328,6 +337,7 @@ const completionConfig = /*@__PURE__*/Facet.define({
aboveCursor: false,
icons: true,
addToOptions: [],
positionInfo: defaultPositionInfo,
compareCompletions: (a, b) => a.label.localeCompare(b.label),
interactionDelay: 75
}, {
@ -343,6 +353,36 @@ const completionConfig = /*@__PURE__*/Facet.define({
function joinClass(a, b) {
return a ? b ? a + " " + b : a : b;
}
function defaultPositionInfo(view, list, option, info, space) {
let rtl = view.textDirection == Direction.RTL, left = rtl, narrow = false;
let side = "top", offset, maxWidth;
let spaceLeft = list.left - space.left, spaceRight = space.right - list.right;
let infoWidth = info.right - info.left, infoHeight = info.bottom - info.top;
if (left && spaceLeft < Math.min(infoWidth, spaceRight))
left = false;
else if (!left && spaceRight < Math.min(infoWidth, spaceLeft))
left = true;
if (infoWidth <= (left ? spaceLeft : spaceRight)) {
offset = Math.max(space.top, Math.min(option.top, space.bottom - infoHeight)) - list.top;
maxWidth = Math.min(400 /* Width */, left ? spaceLeft : spaceRight);
}
else {
narrow = true;
maxWidth = Math.min(400 /* Width */, (rtl ? list.right : space.right - list.left) - 30 /* Margin */);
let spaceBelow = space.bottom - list.bottom;
if (spaceBelow >= infoHeight || spaceBelow > list.top) { // Below the completion
offset = option.bottom - list.top;
}
else { // Above it
side = "bottom";
offset = list.bottom - option.top;
}
}
return {
style: `${side}: ${offset}px; max-width: ${maxWidth}px`,
class: "cm-completionInfo-" + (narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right")
};
}
function optionContent(config) {
let content = config.addToOptions.slice();
@ -362,8 +402,8 @@ function optionContent(config) {
render(completion, _s, match) {
let labelElt = document.createElement("span");
labelElt.className = "cm-completionLabel";
let { label } = completion, off = 0;
for (let j = 1; j < match.length;) {
let label = completion.displayLabel || completion.label, off = 0;
for (let j = 0; j < match.length;) {
let from = match[j++], to = match[j++];
if (from > off)
labelElt.appendChild(document.createTextNode(label.slice(off, from)));
@ -403,13 +443,15 @@ function rangeAroundSelected(total, selected, max) {
return { from: total - (off + 1) * max, to: total - off * max };
}
class CompletionTooltip {
constructor(view, stateField) {
constructor(view, stateField, applyCompletion) {
this.view = view;
this.stateField = stateField;
this.applyCompletion = applyCompletion;
this.info = null;
this.placeInfo = {
this.infoDestroy = null;
this.placeInfoReq = {
read: () => this.measureInfo(),
write: (pos) => this.positionInfo(pos),
write: (pos) => this.placeInfo(pos),
key: this
};
this.space = null;
@ -427,16 +469,22 @@ class CompletionTooltip {
this.dom.addEventListener("mousedown", (e) => {
for (let dom = e.target, match; dom && dom != this.dom; dom = dom.parentNode) {
if (dom.nodeName == "LI" && (match = /-(\d+)$/.exec(dom.id)) && +match[1] < options.length) {
applyCompletion(view, options[+match[1]]);
this.applyCompletion(view, options[+match[1]]);
e.preventDefault();
return;
}
}
});
this.dom.addEventListener("focusout", (e) => {
let state = view.state.field(this.stateField, false);
if (state && state.tooltip && view.state.facet(completionConfig).closeOnBlur &&
e.relatedTarget != view.contentDOM)
view.dispatch({ effects: closeCompletionEffect.of(null) });
});
this.list = this.dom.appendChild(this.createListBox(options, cState.id, this.range));
this.list.addEventListener("scroll", () => {
if (this.info)
this.view.requestMeasure(this.placeInfo);
this.view.requestMeasure(this.placeInfoReq);
});
}
mount() { this.updateSel(); }
@ -466,7 +514,7 @@ class CompletionTooltip {
positioned(space) {
this.space = space;
if (this.info)
this.view.requestMeasure(this.placeInfo);
this.view.requestMeasure(this.placeInfoReq);
}
updateSel() {
let cState = this.view.state.field(this.stateField), open = cState.open;
@ -476,43 +524,52 @@ class CompletionTooltip {
this.list = this.dom.appendChild(this.createListBox(open.options, cState.id, this.range));
this.list.addEventListener("scroll", () => {
if (this.info)
this.view.requestMeasure(this.placeInfo);
this.view.requestMeasure(this.placeInfoReq);
});
}
if (this.updateSelectedOption(open.selected)) {
if (this.info) {
this.info.remove();
this.info = null;
}
this.destroyInfo();
let { completion } = open.options[open.selected];
let { info } = completion;
if (!info)
return;
let infoResult = typeof info === 'string' ? document.createTextNode(info) : info(completion);
let infoResult = typeof info === "string" ? document.createTextNode(info) : info(completion);
if (!infoResult)
return;
if ('then' in infoResult) {
infoResult.then(node => {
if (node && this.view.state.field(this.stateField, false) == cState)
this.addInfoPane(node);
if ("then" in infoResult) {
infoResult.then(obj => {
if (obj && this.view.state.field(this.stateField, false) == cState)
this.addInfoPane(obj, completion);
}).catch(e => logException(this.view.state, e, "completion info"));
}
else {
this.addInfoPane(infoResult);
this.addInfoPane(infoResult, completion);
}
}
}
addInfoPane(content) {
let dom = this.info = document.createElement("div");
dom.className = "cm-tooltip cm-completionInfo";
dom.appendChild(content);
this.dom.appendChild(dom);
this.view.requestMeasure(this.placeInfo);
addInfoPane(content, completion) {
this.destroyInfo();
let wrap = this.info = document.createElement("div");
wrap.className = "cm-tooltip cm-completionInfo";
if (content.nodeType != null) {
wrap.appendChild(content);
this.infoDestroy = null;
}
else {
let { dom, destroy } = content;
wrap.appendChild(dom);
this.infoDestroy = destroy || null;
}
this.dom.appendChild(wrap);
this.view.requestMeasure(this.placeInfoReq);
}
updateSelectedOption(selected) {
let set = null;
for (let opt = this.list.firstChild, i = this.range.from; opt; opt = opt.nextSibling, i++) {
if (i == selected) {
if (opt.nodeName != "LI" || !opt.id) {
i--; // A section header
}
else if (i == selected) {
if (!opt.hasAttribute("aria-selected")) {
opt.setAttribute("aria-selected", "true");
set = opt;
@ -542,41 +599,17 @@ class CompletionTooltip {
if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 ||
selRect.bottom < Math.max(space.top, listRect.top) + 10)
return null;
let rtl = this.view.textDirection == Direction.RTL, left = rtl, narrow = false, maxWidth;
let top = "", bottom = "";
let spaceLeft = listRect.left - space.left, spaceRight = space.right - listRect.right;
if (left && spaceLeft < Math.min(infoRect.width, spaceRight))
left = false;
else if (!left && spaceRight < Math.min(infoRect.width, spaceLeft))
left = true;
if (infoRect.width <= (left ? spaceLeft : spaceRight)) {
top = (Math.max(space.top, Math.min(selRect.top, space.bottom - infoRect.height)) - listRect.top) + "px";
maxWidth = Math.min(400 /* Info.Width */, left ? spaceLeft : spaceRight) + "px";
}
else {
narrow = true;
maxWidth = Math.min(400 /* Info.Width */, (rtl ? listRect.right : space.right - listRect.left) - 30 /* Info.Margin */) + "px";
let spaceBelow = space.bottom - listRect.bottom;
if (spaceBelow >= infoRect.height || spaceBelow > listRect.top) // Below the completion
top = (selRect.bottom - listRect.top) + "px";
else // Above it
bottom = (listRect.bottom - selRect.top) + "px";
}
return {
top, bottom, maxWidth,
class: narrow ? (rtl ? "left-narrow" : "right-narrow") : left ? "left" : "right",
};
return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space);
}
positionInfo(pos) {
placeInfo(pos) {
if (this.info) {
if (pos) {
this.info.style.top = pos.top;
this.info.style.bottom = pos.bottom;
this.info.style.maxWidth = pos.maxWidth;
this.info.className = "cm-tooltip cm-completionInfo cm-completionInfo-" + pos.class;
if (pos.style)
this.info.style.cssText = pos.style;
this.info.className = "cm-tooltip cm-completionInfo " + (pos.class || "");
}
else {
this.info.style.top = "-1e6px";
this.info.style.cssText = "top: -1e6px";
}
}
}
@ -586,8 +619,22 @@ class CompletionTooltip {
ul.setAttribute("role", "listbox");
ul.setAttribute("aria-expanded", "true");
ul.setAttribute("aria-label", this.view.state.phrase("Completions"));
let curSection = null;
for (let i = range.from; i < range.to; i++) {
let { completion, match } = options[i];
let { completion, match } = options[i], { section } = completion;
if (section) {
let name = typeof section == "string" ? section : section.name;
if (name != curSection && (i > range.from || range.from == 0)) {
curSection = name;
if (typeof section != "string" && section.header) {
ul.appendChild(section.header(section));
}
else {
let header = ul.appendChild(document.createElement("completion-section"));
header.textContent = name;
}
}
}
const li = ul.appendChild(document.createElement("li"));
li.id = id + "-" + i;
li.setAttribute("role", "option");
@ -606,11 +653,22 @@ class CompletionTooltip {
ul.classList.add("cm-completionListIncompleteBottom");
return ul;
}
destroyInfo() {
if (this.info) {
if (this.infoDestroy)
this.infoDestroy();
this.info.remove();
this.info = null;
}
}
destroy() {
this.destroyInfo();
}
}
// We allocate a new function instance every time the completion
// changes to force redrawing/repositioning of the tooltip
function completionTooltip(stateField) {
return (view) => new CompletionTooltip(view, stateField);
function completionTooltip(stateField, applyCompletion) {
return (view) => new CompletionTooltip(view, stateField, applyCompletion);
}
function scrollIntoView(container, element) {
let parent = container.getBoundingClientRect();
@ -628,35 +686,56 @@ function score(option) {
(option.type ? 1 : 0);
}
function sortOptions(active, state) {
let options = [], i = 0;
let options = [];
let sections = null;
let addOption = (option) => {
options.push(option);
let { section } = option.completion;
if (section) {
if (!sections)
sections = [];
let name = typeof section == "string" ? section : section.name;
if (!sections.some(s => s.name == name))
sections.push(typeof section == "string" ? { name } : section);
}
};
for (let a of active)
if (a.hasResult()) {
let getMatch = a.result.getMatch;
if (a.result.filter === false) {
let getMatch = a.result.getMatch;
for (let option of a.result.options) {
let match = [1e9 - i++];
if (getMatch)
for (let n of getMatch(option))
match.push(n);
options.push(new Option(option, a, match));
addOption(new Option(option, a.source, getMatch ? getMatch(option) : [], 1e9 - options.length));
}
}
else {
let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to)), match;
let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to));
for (let option of a.result.options)
if (match = matcher.match(option.label)) {
if (option.boost != null)
match[0] += option.boost;
options.push(new Option(option, a, match));
if (matcher.match(option.label)) {
let matched = !option.displayLabel ? matcher.matched : getMatch ? getMatch(option, matcher.matched) : [];
addOption(new Option(option, a.source, matched, matcher.score + (option.boost || 0)));
}
}
}
if (sections) {
let sectionOrder = Object.create(null), pos = 0;
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); };
for (let s of sections.sort(cmp)) {
pos -= 1e5;
sectionOrder[s.name] = pos;
}
for (let option of options) {
let { section } = option.completion;
if (section)
option.score += sectionOrder[typeof section == "string" ? section : section.name];
}
}
let result = [], prev = null;
let compare = state.facet(completionConfig).compareCompletions;
for (let opt of options.sort((a, b) => (b.match[0] - a.match[0]) || compare(a.completion, b.completion))) {
if (!prev || prev.label != opt.completion.label || prev.detail != opt.completion.detail ||
(prev.type != null && opt.completion.type != null && prev.type != opt.completion.type) ||
prev.apply != opt.completion.apply)
for (let opt of options.sort((a, b) => (b.score - a.score) || compare(a.completion, b.completion))) {
let cur = opt.completion;
if (!prev || prev.label != cur.label || prev.detail != cur.detail ||
(prev.type != null && cur.type != null && prev.type != cur.type) ||
prev.apply != cur.apply || prev.boost != cur.boost)
result.push(opt);
else if (score(opt.completion) > score(prev))
result[result.length - 1] = opt;
@ -680,7 +759,7 @@ class CompletionDialog {
static build(active, state, id, prev, conf) {
let options = sortOptions(active, state);
if (!options.length) {
return prev && active.some(a => a.state == 1 /* State.Pending */) ?
return prev && active.some(a => a.state == 1 /* Pending */) ?
new CompletionDialog(prev.options, prev.attrs, prev.tooltip, prev.timestamp, prev.selected, true) : null;
}
let selected = state.facet(completionConfig).selectOnOpen ? 0 : -1;
@ -694,7 +773,7 @@ class CompletionDialog {
}
return new CompletionDialog(options, makeAttrs(id, selected), {
pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
create: completionTooltip(completionState),
create: completionTooltip(completionState, applyCompletion),
above: conf.aboveCursor,
}, prev ? prev.timestamp : Date.now(), selected, false);
}
@ -717,7 +796,7 @@ class CompletionState {
state.languageDataAt("autocomplete", cur(state)).map(asSource);
let active = sources.map(source => {
let value = this.active.find(s => s.source == source) ||
new ActiveSource(source, this.active.some(a => a.state != 0 /* State.Inactive */) ? 1 /* State.Pending */ : 0 /* State.Inactive */);
new ActiveSource(source, this.active.some(a => a.state != 0 /* Inactive */) ? 1 /* Pending */ : 0 /* Inactive */);
return value.update(tr, conf);
});
if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
@ -728,10 +807,10 @@ class CompletionState {
if (tr.selection || active.some(a => a.hasResult() && tr.changes.touchesRange(a.from, a.to)) ||
!sameResults(active, this.active))
open = CompletionDialog.build(active, state, this.id, open, conf);
else if (open && open.disabled && !active.some(a => a.state == 1 /* State.Pending */))
else if (open && open.disabled && !active.some(a => a.state == 1 /* Pending */))
open = null;
if (!open && active.every(a => a.state != 1 /* State.Pending */) && active.some(a => a.hasResult()))
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* State.Inactive */) : a);
if (!open && active.every(a => a.state != 1 /* Pending */) && active.some(a => a.hasResult()))
active = active.map(a => a.hasResult() ? new ActiveSource(a.source, 0 /* Inactive */) : a);
for (let effect of tr.effects)
if (effect.is(setSelectedEffect))
open = open && open.setSelected(effect.value, this.id);
@ -785,13 +864,13 @@ class ActiveSource {
value = value.handleUserEvent(tr, event, conf);
else if (tr.docChanged)
value = value.handleChange(tr);
else if (tr.selection && value.state != 0 /* State.Inactive */)
value = new ActiveSource(value.source, 0 /* State.Inactive */);
else if (tr.selection && value.state != 0 /* Inactive */)
value = new ActiveSource(value.source, 0 /* Inactive */);
for (let effect of tr.effects) {
if (effect.is(startCompletionEffect))
value = new ActiveSource(value.source, 1 /* State.Pending */, effect.value ? cur(tr.state) : -1);
value = new ActiveSource(value.source, 1 /* Pending */, effect.value ? cur(tr.state) : -1);
else if (effect.is(closeCompletionEffect))
value = new ActiveSource(value.source, 0 /* State.Inactive */);
value = new ActiveSource(value.source, 0 /* Inactive */);
else if (effect.is(setActiveEffect))
for (let active of effect.value)
if (active.source == value.source)
@ -800,10 +879,10 @@ class ActiveSource {
return value;
}
handleUserEvent(tr, type, conf) {
return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* State.Pending */);
return type == "delete" || !conf.activateOnTyping ? this.map(tr.changes) : new ActiveSource(this.source, 1 /* Pending */);
}
handleChange(tr) {
return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
return tr.changes.touchesRange(cur(tr.startState)) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
}
map(changes) {
return changes.empty || this.explicitPos < 0 ? this : new ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos));
@ -811,7 +890,7 @@ class ActiveSource {
}
class ActiveResult extends ActiveSource {
constructor(source, explicitPos, result, from, to) {
super(source, 2 /* State.Result */, explicitPos);
super(source, 2 /* Result */, explicitPos);
this.result = result;
this.from = from;
this.to = to;
@ -824,17 +903,17 @@ class ActiveResult extends ActiveSource {
if ((this.explicitPos < 0 ? pos <= from : pos < this.from) ||
pos > to ||
type == "delete" && cur(tr.startState) == this.from)
return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* State.Pending */ : 0 /* State.Inactive */);
return new ActiveSource(this.source, type == "input" && conf.activateOnTyping ? 1 /* Pending */ : 0 /* Inactive */);
let explicitPos = this.explicitPos < 0 ? -1 : tr.changes.mapPos(this.explicitPos), updated;
if (checkValid(this.result.validFor, tr.state, from, to))
return new ActiveResult(this.source, explicitPos, this.result, from, to);
if (this.result.update &&
(updated = this.result.update(this.result, from, to, new CompletionContext(tr.state, pos, explicitPos >= 0))))
return new ActiveResult(this.source, explicitPos, updated, updated.from, (_a = updated.to) !== null && _a !== void 0 ? _a : cur(tr.state));
return new ActiveSource(this.source, 1 /* State.Pending */, explicitPos);
return new ActiveSource(this.source, 1 /* Pending */, explicitPos);
}
handleChange(tr) {
return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* State.Inactive */) : this.map(tr.changes);
return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(this.source, 0 /* Inactive */) : this.map(tr.changes);
}
map(mapping) {
return mapping.empty ? this :
@ -847,8 +926,6 @@ function checkValid(validFor, state, from, to) {
let text = state.sliceDoc(from, to);
return typeof validFor == "function" ? validFor(text, from, to, state) : ensureAnchor(validFor, true).test(text);
}
const startCompletionEffect = /*@__PURE__*/StateEffect.define();
const closeCompletionEffect = /*@__PURE__*/StateEffect.define();
const setActiveEffect = /*@__PURE__*/StateEffect.define({
map(sources, mapping) { return sources.map(s => s.map(mapping)); }
});
@ -861,6 +938,17 @@ const completionState = /*@__PURE__*/StateField.define({
EditorView.contentAttributes.from(f, state => state.attrs)
]
});
function applyCompletion(view, option) {
const apply = option.completion.apply || option.completion.label;
let result = view.state.field(completionState).active.find(a => a.source == option.source);
if (!(result instanceof ActiveResult))
return false;
if (typeof apply == "string")
view.dispatch(Object.assign(Object.assign({}, insertCompletionText(view.state, apply, result.from, result.to)), { annotations: pickedCompletion.of(option.completion) }));
else
apply(view, option.completion, result.from, result.to);
return true;
}
/**
Returns a command that moves the completion selection forward or
@ -891,12 +979,10 @@ Accept the current completion.
*/
const acceptCompletion = (view) => {
let cState = view.state.field(completionState, false);
if (view.state.readOnly || !cState || !cState.open || cState.open.selected < 0 ||
if (view.state.readOnly || !cState || !cState.open || cState.open.selected < 0 || cState.open.disabled ||
Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
return false;
if (!cState.open.disabled)
applyCompletion(view, cState.open.options[cState.open.selected]);
return true;
return applyCompletion(view, cState.open.options[cState.open.selected]);
};
/**
Explicitly start autocompletion.
@ -913,7 +999,7 @@ Close the currently active completion.
*/
const closeCompletion = (view) => {
let cState = view.state.field(completionState, false);
if (!cState || !cState.active.some(a => a.state != 0 /* State.Inactive */))
if (!cState || !cState.active.some(a => a.state != 0 /* Inactive */))
return false;
view.dispatch({ effects: closeCompletionEffect.of(null) });
return true;
@ -936,9 +1022,9 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
this.debounceUpdate = -1;
this.running = [];
this.debounceAccept = -1;
this.composing = 0 /* CompositionState.None */;
this.composing = 0 /* None */;
for (let active of view.state.field(completionState).active)
if (active.state == 1 /* State.Pending */)
if (active.state == 1 /* Pending */)
this.startQuery(active);
}
update(update) {
@ -969,21 +1055,21 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
}
if (this.debounceUpdate > -1)
clearTimeout(this.debounceUpdate);
this.debounceUpdate = cState.active.some(a => a.state == 1 /* State.Pending */ && !this.running.some(q => q.active.source == a.source))
this.debounceUpdate = cState.active.some(a => a.state == 1 /* Pending */ && !this.running.some(q => q.active.source == a.source))
? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
if (this.composing != 0 /* CompositionState.None */)
if (this.composing != 0 /* None */)
for (let tr of update.transactions) {
if (getUserEvent(tr) == "input")
this.composing = 2 /* CompositionState.Changed */;
else if (this.composing == 2 /* CompositionState.Changed */ && tr.selection)
this.composing = 3 /* CompositionState.ChangedAndMoved */;
this.composing = 2 /* Changed */;
else if (this.composing == 2 /* Changed */ && tr.selection)
this.composing = 3 /* ChangedAndMoved */;
}
}
startUpdate() {
this.debounceUpdate = -1;
let { state } = this.view, cState = state.field(completionState);
for (let active of cState.active) {
if (active.state == 1 /* State.Pending */ && !this.running.some(r => r.active.source == active.source))
if (active.state == 1 /* Pending */ && !this.running.some(r => r.active.source == active.source))
this.startQuery(active);
}
}
@ -1034,14 +1120,14 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
}
}
let current = this.view.state.field(completionState).active.find(a => a.source == query.active.source);
if (current && current.state == 1 /* State.Pending */) {
if (current && current.state == 1 /* Pending */) {
if (query.done == null) {
// Explicitly failed. Should clear the pending status if it
// hasn't been re-set in the meantime.
let active = new ActiveSource(query.active.source, 0 /* State.Inactive */);
let active = new ActiveSource(query.active.source, 0 /* Inactive */);
for (let tr of query.updates)
active = active.update(tr, conf);
if (active.state != 1 /* State.Pending */)
if (active.state != 1 /* Pending */)
updated.push(active);
}
else {
@ -1055,21 +1141,24 @@ const completionPlugin = /*@__PURE__*/ViewPlugin.fromClass(class {
}
}, {
eventHandlers: {
blur() {
blur(event) {
let state = this.view.state.field(completionState, false);
if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur)
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
if (state && state.tooltip && this.view.state.facet(completionConfig).closeOnBlur) {
let dialog = state.open && getTooltip(this.view, state.open.tooltip);
if (!dialog || !dialog.dom.contains(event.relatedTarget))
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
}
},
compositionstart() {
this.composing = 1 /* CompositionState.Started */;
this.composing = 1 /* Started */;
},
compositionend() {
if (this.composing == 3 /* CompositionState.ChangedAndMoved */) {
if (this.composing == 3 /* ChangedAndMoved */) {
// Safari fires compositionend events synchronously, possibly
// from inside an update, so dispatch asynchronously to avoid reentrancy
setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
}
this.composing = 0 /* CompositionState.None */;
this.composing = 0 /* None */;
}
}
});
@ -1088,13 +1177,21 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
listStyle: "none",
margin: 0,
padding: 0,
"& > li": {
overflowX: "hidden",
textOverflow: "ellipsis",
cursor: "pointer",
"& > li, & > completion-section": {
padding: "1px 3px",
lineHeight: 1.2
},
"& > li": {
overflowX: "hidden",
textOverflow: "ellipsis",
cursor: "pointer"
},
"& > completion-section": {
display: "list-item",
borderBottom: "1px solid silver",
paddingLeft: "0.5em",
opacity: 0.7
}
}
},
"&light .cm-tooltip-autocomplete ul li[aria-selected]": {
@ -1121,13 +1218,13 @@ const baseTheme = /*@__PURE__*/EditorView.baseTheme({
position: "absolute",
padding: "3px 9px",
width: "max-content",
maxWidth: `${400 /* Info.Width */}px`,
maxWidth: `${400 /* Width */}px`,
boxSizing: "border-box"
},
".cm-completionInfo.cm-completionInfo-left": { right: "100%" },
".cm-completionInfo.cm-completionInfo-right": { left: "100%" },
".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Info.Margin */}px` },
".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Info.Margin */}px` },
".cm-completionInfo.cm-completionInfo-left-narrow": { right: `${30 /* Margin */}px` },
".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30 /* Margin */}px` },
"&light .cm-snippetField": { backgroundColor: "#00000022" },
"&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
".cm-snippetFieldPosition": {
@ -1352,11 +1449,12 @@ interpreted as indicating a placeholder.
*/
function snippet(template) {
let snippet = Snippet.parse(template);
return (editor, _completion, from, to) => {
return (editor, completion, from, to) => {
let { text, ranges } = snippet.instantiate(editor.state, from);
let spec = {
changes: { from, to, insert: Text.of(text) },
scrollIntoView: true
scrollIntoView: true,
annotations: completion ? pickedCompletion.of(completion) : undefined
};
if (ranges.length)
spec.selection = fieldSelection(ranges, 0);
@ -1400,6 +1498,22 @@ const nextSnippetField = /*@__PURE__*/moveField(1);
Move to the previous snippet field, if available.
*/
const prevSnippetField = /*@__PURE__*/moveField(-1);
/**
Check if there is an active snippet with a next field for
`nextSnippetField` to move to.
*/
function hasNextSnippetField(state) {
let active = state.field(snippetState, false);
return !!(active && active.ranges.some(r => r.field == active.active + 1));
}
/**
Returns true if there is an active snippet and a previous field
for `prevSnippetField` to move to.
*/
function hasPrevSnippetField(state) {
let active = state.field(snippetState, false);
return !!(active && active.active > 0);
}
const defaultSnippetKeymap = [
{ key: "Tab", run: nextSnippetField, shift: prevSnippetField },
{ key: "Escape", run: clearSnippet }
@ -1463,7 +1577,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
if (!seen[m[0]] && pos + m.index != ignoreAt) {
result.push({ type: "text", label: m[0] });
seen[m[0]] = true;
if (result.length >= 2000 /* C.MaxList */)
if (result.length >= 2000 /* MaxList */)
return;
}
}
@ -1471,7 +1585,7 @@ function storeWords(doc, wordRE, result, seen, ignoreAt) {
}
}
function collectWords(doc, cache, wordRE, to, ignoreAt) {
let big = doc.length >= 1000 /* C.MinCacheLen */;
let big = doc.length >= 1000 /* MinCacheLen */;
let cached = big && cache.get(doc);
if (cached)
return cached;
@ -1479,7 +1593,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
if (doc.children) {
let pos = 0;
for (let ch of doc.children) {
if (ch.length >= 1000 /* C.MinCacheLen */) {
if (ch.length >= 1000 /* MinCacheLen */) {
for (let c of collectWords(ch, cache, wordRE, to - pos, ignoreAt - pos)) {
if (!seen[c.label]) {
seen[c.label] = true;
@ -1496,7 +1610,7 @@ function collectWords(doc, cache, wordRE, to, ignoreAt) {
else {
storeWords(doc, wordRE, result, seen, ignoreAt);
}
if (big && result.length < 2000 /* C.MaxList */)
if (big && result.length < 2000 /* MaxList */)
cache.set(doc, result);
return result;
}
@ -1512,7 +1626,7 @@ const completeAnyWord = context => {
if (!token && !context.explicit)
return null;
let from = token ? token.from : context.pos;
let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* C.Range */, from);
let options = collectWords(context.state.doc, wordCache(wordChars), re, 50000 /* Range */, from);
return { from, options, validFor: mapRE(re, s => "^" + s) };
};
@ -1527,9 +1641,6 @@ const closeBracketEffect = /*@__PURE__*/StateEffect.define({
return mapped == null ? undefined : mapped;
}
});
const skipBracketEffect = /*@__PURE__*/StateEffect.define({
map(value, mapping) { return mapping.mapPos(value); }
});
const closedBracket = /*@__PURE__*/new class extends RangeValue {
};
closedBracket.startSide = 1;
@ -1544,12 +1655,9 @@ const bracketState = /*@__PURE__*/StateField.define({
value = RangeSet.empty;
}
value = value.map(tr.changes);
for (let effect of tr.effects) {
for (let effect of tr.effects)
if (effect.is(closeBracketEffect))
value = value.update({ add: [closedBracket.range(effect.value, effect.value + 1)] });
else if (effect.is(skipBracketEffect))
value = value.update({ filter: from => from != effect.value });
}
return value;
}
});
@ -1677,15 +1785,15 @@ function handleOpen(state, open, close, closeBefore) {
});
}
function handleClose(state, _open, close) {
let dont = null, moved = state.selection.ranges.map(range => {
let dont = null, changes = state.changeByRange(range => {
if (range.empty && nextChar(state.doc, range.head) == close)
return EditorSelection.cursor(range.head + close.length);
return dont = range;
return { changes: { from: range.head, to: range.head + close.length, insert: close },
range: EditorSelection.cursor(range.head + close.length) };
return dont = { range };
});
return dont ? null : state.update({
selection: EditorSelection.create(moved, state.selection.mainIndex),
return dont ? null : state.update(changes, {
scrollIntoView: true,
effects: state.selection.ranges.map(({ from }) => skipBracketEffect.of(from))
userEvent: "input.type"
});
}
// Handles cases where the open and close token are the same, and
@ -1706,8 +1814,9 @@ function handleSame(state, token, allowTriple, config) {
}
else if (closedBracketAt(state, pos)) {
let isTriple = allowTriple && state.sliceDoc(pos, pos + token.length * 3) == token + token + token;
return { range: EditorSelection.cursor(pos + token.length * (isTriple ? 3 : 1)),
effects: skipBracketEffect.of(pos) };
let content = isTriple ? token + token + token : token;
return { changes: { from: pos, to: pos + content.length, insert: content },
range: EditorSelection.cursor(pos + content.length) };
}
}
else if (allowTriple && state.sliceDoc(pos - 2 * token.length, pos) == token + token &&
@ -1809,8 +1918,8 @@ returns `null`.
*/
function completionStatus(state) {
let cState = state.field(completionState, false);
return cState && cState.active.some(a => a.state == 1 /* State.Pending */) ? "pending"
: cState && cState.active.some(a => a.state != 0 /* State.Inactive */) ? "active" : null;
return cState && cState.active.some(a => a.state == 1 /* Pending */) ? "pending"
: cState && cState.active.some(a => a.state != 0 /* Inactive */) ? "active" : null;
}
const completionArrayCache = /*@__PURE__*/new WeakMap;
/**
@ -1851,4 +1960,4 @@ function setSelectedCompletion(index) {
return setSelectedEffect.of(index);
}
export { CompletionContext, 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 };
export { CompletionContext, acceptCompletion, autocompletion, clearSnippet, closeBrackets, closeBracketsKeymap, closeCompletion, completeAnyWord, completeFromList, completionKeymap, completionStatus, currentCompletions, deleteBracketPair, hasNextSnippetField, hasPrevSnippetField, ifIn, ifNotIn, insertBracket, insertCompletionText, moveCompletionSelection, nextSnippetField, pickedCompletion, prevSnippetField, selectedCompletion, selectedCompletionIndex, setSelectedCompletion, snippet, snippetCompletion, snippetKeymap, startCompletion };

View file

@ -1,6 +1,6 @@
{
"name": "@codemirror/autocomplete",
"version": "6.4.0",
"version": "6.9.0",
"description": "Autocompletion for the CodeMirror code editor",
"scripts": {
"test": "cm-runtests",
@ -12,7 +12,7 @@
],
"author": {
"name": "Marijn Haverbeke",
"email": "marijnh@gmail.com",
"email": "marijn@haverbeke.berlin",
"url": "http://marijnhaverbeke.nl"
},
"type": "module",
@ -38,7 +38,7 @@
"@lezer/common": "^1.0.0"
},
"devDependencies": {
"@codemirror/buildhelper": "^0.1.5"
"@codemirror/buildhelper": "^1.0.0"
},
"repository": {
"type": "git",