mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-05 19:42:38 +02:00
npm update
This commit is contained in:
parent
0cdd3e9fee
commit
4696ba952f
1437 changed files with 32727 additions and 1248226 deletions
80
node_modules/@codemirror/autocomplete/CHANGELOG.md
generated
vendored
80
node_modules/@codemirror/autocomplete/CHANGELOG.md
generated
vendored
|
@ -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
|
||||
|
|
2
node_modules/@codemirror/autocomplete/LICENSE
generated
vendored
2
node_modules/@codemirror/autocomplete/LICENSE
generated
vendored
|
@ -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
|
||||
|
|
495
node_modules/@codemirror/autocomplete/dist/index.cjs
generated
vendored
495
node_modules/@codemirror/autocomplete/dist/index.cjs
generated
vendored
|
@ -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
567
node_modules/@codemirror/autocomplete/dist/index.d.cts
generated
vendored
Normal 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 sources—in 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 source—the 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 brackets—the
|
||||
[`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 };
|
274
node_modules/@codemirror/autocomplete/dist/index.d.ts
generated
vendored
274
node_modules/@codemirror/autocomplete/dist/index.d.ts
generated
vendored
|
@ -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 };
|
||||
|
|
499
node_modules/@codemirror/autocomplete/dist/index.js
generated
vendored
499
node_modules/@codemirror/autocomplete/dist/index.js
generated
vendored
|
@ -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 };
|
||||
|
|
6
node_modules/@codemirror/autocomplete/package.json
generated
vendored
6
node_modules/@codemirror/autocomplete/package.json
generated
vendored
|
@ -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",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue