1877 lines
65 KiB
JavaScript
1877 lines
65 KiB
JavaScript
import {
|
||
indentUnit,
|
||
syntaxTree
|
||
} from "./chunk-D63OSQ34.js";
|
||
import {
|
||
Decoration,
|
||
Direction,
|
||
EditorView,
|
||
ViewPlugin,
|
||
WidgetType,
|
||
getTooltip,
|
||
keymap,
|
||
logException,
|
||
showTooltip
|
||
} from "./chunk-LORPBXGU.js";
|
||
import {
|
||
Annotation,
|
||
CharCategory,
|
||
EditorSelection,
|
||
Facet,
|
||
MapMode,
|
||
Prec,
|
||
RangeSet,
|
||
RangeValue,
|
||
StateEffect,
|
||
StateField,
|
||
Text,
|
||
codePointAt,
|
||
codePointSize,
|
||
combineConfig,
|
||
fromCodePoint
|
||
} from "./chunk-MKFMOIK6.js";
|
||
|
||
// node_modules/@codemirror/autocomplete/dist/index.js
|
||
var CompletionContext = class {
|
||
/**
|
||
Create a new completion context. (Mostly useful for testing
|
||
completion sources—in the editor, the extension will create
|
||
these for you.)
|
||
*/
|
||
constructor(state, pos, explicit) {
|
||
this.state = state;
|
||
this.pos = pos;
|
||
this.explicit = explicit;
|
||
this.abortListeners = [];
|
||
}
|
||
/**
|
||
Get the extent, content, and (if there is a token) type of the
|
||
token before `this.pos`.
|
||
*/
|
||
tokenBefore(types) {
|
||
let token = syntaxTree(this.state).resolveInner(this.pos, -1);
|
||
while (token && types.indexOf(token.name) < 0)
|
||
token = token.parent;
|
||
return token ? {
|
||
from: token.from,
|
||
to: this.pos,
|
||
text: this.state.sliceDoc(token.from, this.pos),
|
||
type: token.type
|
||
} : null;
|
||
}
|
||
/**
|
||
Get the match of the given expression directly before the
|
||
cursor.
|
||
*/
|
||
matchBefore(expr) {
|
||
let line = this.state.doc.lineAt(this.pos);
|
||
let start = Math.max(line.from, this.pos - 250);
|
||
let str = line.text.slice(start - line.from, this.pos - line.from);
|
||
let found = str.search(ensureAnchor(expr, false));
|
||
return found < 0 ? null : { from: start + found, to: this.pos, text: str.slice(found) };
|
||
}
|
||
/**
|
||
Yields true when the query has been aborted. Can be useful in
|
||
asynchronous queries to avoid doing work that will be ignored.
|
||
*/
|
||
get aborted() {
|
||
return this.abortListeners == null;
|
||
}
|
||
/**
|
||
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, listener) {
|
||
if (type == "abort" && this.abortListeners)
|
||
this.abortListeners.push(listener);
|
||
}
|
||
};
|
||
function toSet(chars) {
|
||
let flat = Object.keys(chars).join("");
|
||
let words = /\w/.test(flat);
|
||
if (words)
|
||
flat = flat.replace(/\w/g, "");
|
||
return `[${words ? "\\w" : ""}${flat.replace(/[^\w\s]/g, "\\$&")}]`;
|
||
}
|
||
function prefixMatch(options) {
|
||
let first = /* @__PURE__ */ Object.create(null), rest = /* @__PURE__ */ Object.create(null);
|
||
for (let { label } of options) {
|
||
first[label[0]] = true;
|
||
for (let i = 1; i < label.length; i++)
|
||
rest[label[i]] = true;
|
||
}
|
||
let source = toSet(first) + toSet(rest) + "*$";
|
||
return [new RegExp("^" + source), new RegExp(source)];
|
||
}
|
||
function completeFromList(list) {
|
||
let options = list.map((o) => typeof o == "string" ? { label: o } : o);
|
||
let [validFor, match] = options.every((o) => /^\w+$/.test(o.label)) ? [/\w*$/, /\w+$/] : prefixMatch(options);
|
||
return (context) => {
|
||
let token = context.matchBefore(match);
|
||
return token || context.explicit ? { from: token ? token.from : context.pos, options, validFor } : null;
|
||
};
|
||
}
|
||
function ifIn(nodes, source) {
|
||
return (context) => {
|
||
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;
|
||
};
|
||
}
|
||
function ifNotIn(nodes, source) {
|
||
return (context) => {
|
||
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);
|
||
};
|
||
}
|
||
var Option = class {
|
||
constructor(completion, source, match, score2) {
|
||
this.completion = completion;
|
||
this.source = source;
|
||
this.match = match;
|
||
this.score = score2;
|
||
}
|
||
};
|
||
function cur(state) {
|
||
return state.selection.main.from;
|
||
}
|
||
function ensureAnchor(expr, start) {
|
||
var _a;
|
||
let { source } = expr;
|
||
let addStart = start && source[0] != "^", addEnd = source[source.length - 1] != "$";
|
||
if (!addStart && !addEnd)
|
||
return expr;
|
||
return new RegExp(`${addStart ? "^" : ""}(?:${source})${addEnd ? "$" : ""}`, (_a = expr.flags) !== null && _a !== void 0 ? _a : expr.ignoreCase ? "i" : "");
|
||
}
|
||
var pickedCompletion = Annotation.define();
|
||
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 != main && from != to && state.sliceDoc(range.from + fromOff, range.from + toOff) != state.sliceDoc(from, to))
|
||
return { range };
|
||
return {
|
||
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" });
|
||
}
|
||
var SourceCache = /* @__PURE__ */ new WeakMap();
|
||
function asSource(source) {
|
||
if (!Array.isArray(source))
|
||
return source;
|
||
let known = SourceCache.get(source);
|
||
if (!known)
|
||
SourceCache.set(source, known = completeFromList(source));
|
||
return known;
|
||
}
|
||
var startCompletionEffect = StateEffect.define();
|
||
var closeCompletionEffect = StateEffect.define();
|
||
var FuzzyMatcher = class {
|
||
constructor(pattern) {
|
||
this.pattern = pattern;
|
||
this.chars = [];
|
||
this.folded = [];
|
||
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);
|
||
let part = pattern.slice(p, p + size), upper = part.toUpperCase();
|
||
this.folded.push(codePointAt(upper == part ? part.toLowerCase() : upper, 0));
|
||
p += size;
|
||
}
|
||
this.astral = pattern.length != this.chars.length;
|
||
}
|
||
ret(score2, matched) {
|
||
this.score = score2;
|
||
this.matched = matched;
|
||
return true;
|
||
}
|
||
// Matches a given word (completion) against the pattern (input).
|
||
// 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 this.ret(-100, []);
|
||
if (word.length < this.pattern.length)
|
||
return false;
|
||
let { chars, folded, any, precise, byWord } = this;
|
||
if (chars.length == 1) {
|
||
let first = codePointAt(word, 0), firstSize = codePointSize(first);
|
||
let score2 = firstSize == word.length ? 0 : -100;
|
||
if (first == chars[0])
|
||
;
|
||
else if (first == folded[0])
|
||
score2 += -200;
|
||
else
|
||
return false;
|
||
return this.ret(score2, [0, firstSize]);
|
||
}
|
||
let direct = word.indexOf(this.pattern);
|
||
if (direct == 0)
|
||
return this.ret(word.length == this.pattern.length ? 0 : -100, [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; ) {
|
||
let next = codePointAt(word, i);
|
||
if (next == chars[anyTo] || next == folded[anyTo])
|
||
any[anyTo++] = i;
|
||
i += codePointSize(next);
|
||
}
|
||
if (anyTo < len)
|
||
return false;
|
||
}
|
||
let preciseTo = 0;
|
||
let byWordTo = 0, byWordFolded = false;
|
||
let adjacentTo = 0, adjacentStart = -1, adjacentEnd = -1;
|
||
let hasLower = /[a-z]/.test(word), wordAdjacent = true;
|
||
for (let i = 0, e = Math.min(word.length, 200), prevType = 0; i < e && byWordTo < len; ) {
|
||
let next = codePointAt(word, i);
|
||
if (direct < 0) {
|
||
if (preciseTo < len && next == chars[preciseTo])
|
||
precise[preciseTo++] = i;
|
||
if (adjacentTo < len) {
|
||
if (next == chars[adjacentTo] || next == folded[adjacentTo]) {
|
||
if (adjacentTo == 0)
|
||
adjacentStart = i;
|
||
adjacentEnd = i + 1;
|
||
adjacentTo++;
|
||
} else {
|
||
adjacentTo = 0;
|
||
}
|
||
}
|
||
}
|
||
let ch, type = next < 255 ? next >= 48 && next <= 57 || next >= 97 && next <= 122 ? 2 : next >= 65 && next <= 90 ? 1 : 0 : (ch = fromCodePoint(next)) != ch.toLowerCase() ? 1 : ch != ch.toUpperCase() ? 2 : 0;
|
||
if (!i || type == 1 && hasLower || prevType == 0 && type != 0) {
|
||
if (chars[byWordTo] == next || folded[byWordTo] == next && (byWordFolded = true))
|
||
byWord[byWordTo++] = i;
|
||
else if (byWord.length)
|
||
wordAdjacent = false;
|
||
}
|
||
prevType = type;
|
||
i += codePointSize(next);
|
||
}
|
||
if (byWordTo == len && byWord[0] == 0 && wordAdjacent)
|
||
return this.result(-100 + (byWordFolded ? -200 : 0), byWord, word);
|
||
if (adjacentTo == len && adjacentStart == 0)
|
||
return this.ret(-200 - word.length + (adjacentEnd == word.length ? 0 : -100), [0, adjacentEnd]);
|
||
if (direct > -1)
|
||
return this.ret(-700 - word.length, [direct, direct + this.pattern.length]);
|
||
if (adjacentTo == len)
|
||
return this.ret(-200 + -700 - word.length, [adjacentStart, adjacentEnd]);
|
||
if (byWordTo == len)
|
||
return this.result(-100 + (byWordFolded ? -200 : 0) + -700 + (wordAdjacent ? 0 : -1100), byWord, word);
|
||
return chars.length == 2 ? false : this.result((any[0] ? -700 : 0) + -200 + -1100, any, word);
|
||
}
|
||
result(score2, positions, word) {
|
||
let result = [], i = 0;
|
||
for (let pos of positions) {
|
||
let to = pos + (this.astral ? codePointSize(codePointAt(word, pos)) : 1);
|
||
if (i && result[i - 1] == pos)
|
||
result[i - 1] = to;
|
||
else {
|
||
result[i++] = pos;
|
||
result[i++] = to;
|
||
}
|
||
}
|
||
return this.ret(score2 - word.length, result);
|
||
}
|
||
};
|
||
var completionConfig = Facet.define({
|
||
combine(configs) {
|
||
return combineConfig(configs, {
|
||
activateOnTyping: true,
|
||
selectOnOpen: true,
|
||
override: null,
|
||
closeOnBlur: true,
|
||
maxRenderedOptions: 100,
|
||
defaultKeymap: true,
|
||
tooltipClass: () => "",
|
||
optionClass: () => "",
|
||
aboveCursor: false,
|
||
icons: true,
|
||
addToOptions: [],
|
||
positionInfo: defaultPositionInfo,
|
||
compareCompletions: (a, b) => a.label.localeCompare(b.label),
|
||
interactionDelay: 75
|
||
}, {
|
||
defaultKeymap: (a, b) => a && b,
|
||
closeOnBlur: (a, b) => a && b,
|
||
icons: (a, b) => a && b,
|
||
tooltipClass: (a, b) => (c) => joinClass(a(c), b(c)),
|
||
optionClass: (a, b) => (c) => joinClass(a(c), b(c)),
|
||
addToOptions: (a, b) => a.concat(b)
|
||
});
|
||
}
|
||
});
|
||
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, left ? spaceLeft : spaceRight);
|
||
} else {
|
||
narrow = true;
|
||
maxWidth = Math.min(
|
||
400,
|
||
(rtl ? list.right : space.right - list.left) - 30
|
||
/* Margin */
|
||
);
|
||
let spaceBelow = space.bottom - list.bottom;
|
||
if (spaceBelow >= infoHeight || spaceBelow > list.top) {
|
||
offset = option.bottom - list.top;
|
||
} else {
|
||
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(config2) {
|
||
let content = config2.addToOptions.slice();
|
||
if (config2.icons)
|
||
content.push({
|
||
render(completion) {
|
||
let icon = document.createElement("div");
|
||
icon.classList.add("cm-completionIcon");
|
||
if (completion.type)
|
||
icon.classList.add(...completion.type.split(/\s+/g).map((cls) => "cm-completionIcon-" + cls));
|
||
icon.setAttribute("aria-hidden", "true");
|
||
return icon;
|
||
},
|
||
position: 20
|
||
});
|
||
content.push({
|
||
render(completion, _s, match) {
|
||
let labelElt = document.createElement("span");
|
||
labelElt.className = "cm-completionLabel";
|
||
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)));
|
||
let span = labelElt.appendChild(document.createElement("span"));
|
||
span.appendChild(document.createTextNode(label.slice(from, to)));
|
||
span.className = "cm-completionMatchedText";
|
||
off = to;
|
||
}
|
||
if (off < label.length)
|
||
labelElt.appendChild(document.createTextNode(label.slice(off)));
|
||
return labelElt;
|
||
},
|
||
position: 50
|
||
}, {
|
||
render(completion) {
|
||
if (!completion.detail)
|
||
return null;
|
||
let detailElt = document.createElement("span");
|
||
detailElt.className = "cm-completionDetail";
|
||
detailElt.textContent = completion.detail;
|
||
return detailElt;
|
||
},
|
||
position: 80
|
||
});
|
||
return content.sort((a, b) => a.position - b.position).map((a) => a.render);
|
||
}
|
||
function rangeAroundSelected(total, selected, max) {
|
||
if (total <= max)
|
||
return { from: 0, to: total };
|
||
if (selected < 0)
|
||
selected = 0;
|
||
if (selected <= total >> 1) {
|
||
let off2 = Math.floor(selected / max);
|
||
return { from: off2 * max, to: (off2 + 1) * max };
|
||
}
|
||
let off = Math.floor((total - selected) / max);
|
||
return { from: total - (off + 1) * max, to: total - off * max };
|
||
}
|
||
var CompletionTooltip = class {
|
||
constructor(view, stateField, applyCompletion2) {
|
||
this.view = view;
|
||
this.stateField = stateField;
|
||
this.applyCompletion = applyCompletion2;
|
||
this.info = null;
|
||
this.infoDestroy = null;
|
||
this.placeInfoReq = {
|
||
read: () => this.measureInfo(),
|
||
write: (pos) => this.placeInfo(pos),
|
||
key: this
|
||
};
|
||
this.space = null;
|
||
this.currentClass = "";
|
||
let cState = view.state.field(stateField);
|
||
let { options, selected } = cState.open;
|
||
let config2 = view.state.facet(completionConfig);
|
||
this.optionContent = optionContent(config2);
|
||
this.optionClass = config2.optionClass;
|
||
this.tooltipClass = config2.tooltipClass;
|
||
this.range = rangeAroundSelected(options.length, selected, config2.maxRenderedOptions);
|
||
this.dom = document.createElement("div");
|
||
this.dom.className = "cm-tooltip-autocomplete";
|
||
this.updateTooltipClass(view.state);
|
||
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) {
|
||
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.placeInfoReq);
|
||
});
|
||
}
|
||
mount() {
|
||
this.updateSel();
|
||
}
|
||
update(update) {
|
||
var _a, _b, _c;
|
||
let cState = update.state.field(this.stateField);
|
||
let prevState = update.startState.field(this.stateField);
|
||
this.updateTooltipClass(update.state);
|
||
if (cState != prevState) {
|
||
this.updateSel();
|
||
if (((_a = cState.open) === null || _a === void 0 ? void 0 : _a.disabled) != ((_b = prevState.open) === null || _b === void 0 ? void 0 : _b.disabled))
|
||
this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!((_c = cState.open) === null || _c === void 0 ? void 0 : _c.disabled));
|
||
}
|
||
}
|
||
updateTooltipClass(state) {
|
||
let cls = this.tooltipClass(state);
|
||
if (cls != this.currentClass) {
|
||
for (let c of this.currentClass.split(" "))
|
||
if (c)
|
||
this.dom.classList.remove(c);
|
||
for (let c of cls.split(" "))
|
||
if (c)
|
||
this.dom.classList.add(c);
|
||
this.currentClass = cls;
|
||
}
|
||
}
|
||
positioned(space) {
|
||
this.space = space;
|
||
if (this.info)
|
||
this.view.requestMeasure(this.placeInfoReq);
|
||
}
|
||
updateSel() {
|
||
let cState = this.view.state.field(this.stateField), open = cState.open;
|
||
if (open.selected > -1 && open.selected < this.range.from || open.selected >= this.range.to) {
|
||
this.range = rangeAroundSelected(open.options.length, open.selected, this.view.state.facet(completionConfig).maxRenderedOptions);
|
||
this.list.remove();
|
||
this.list = this.dom.appendChild(this.createListBox(open.options, cState.id, this.range));
|
||
this.list.addEventListener("scroll", () => {
|
||
if (this.info)
|
||
this.view.requestMeasure(this.placeInfoReq);
|
||
});
|
||
}
|
||
if (this.updateSelectedOption(open.selected)) {
|
||
this.destroyInfo();
|
||
let { completion } = open.options[open.selected];
|
||
let { info } = completion;
|
||
if (!info)
|
||
return;
|
||
let infoResult = typeof info === "string" ? document.createTextNode(info) : info(completion);
|
||
if (!infoResult)
|
||
return;
|
||
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, completion);
|
||
}
|
||
}
|
||
}
|
||
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 (opt.nodeName != "LI" || !opt.id) {
|
||
i--;
|
||
} else if (i == selected) {
|
||
if (!opt.hasAttribute("aria-selected")) {
|
||
opt.setAttribute("aria-selected", "true");
|
||
set = opt;
|
||
}
|
||
} else {
|
||
if (opt.hasAttribute("aria-selected"))
|
||
opt.removeAttribute("aria-selected");
|
||
}
|
||
}
|
||
if (set)
|
||
scrollIntoView(this.list, set);
|
||
return set;
|
||
}
|
||
measureInfo() {
|
||
let sel = this.dom.querySelector("[aria-selected]");
|
||
if (!sel || !this.info)
|
||
return null;
|
||
let listRect = this.dom.getBoundingClientRect();
|
||
let infoRect = this.info.getBoundingClientRect();
|
||
let selRect = sel.getBoundingClientRect();
|
||
let space = this.space;
|
||
if (!space) {
|
||
let win = this.dom.ownerDocument.defaultView || window;
|
||
space = { left: 0, top: 0, right: win.innerWidth, bottom: win.innerHeight };
|
||
}
|
||
if (selRect.top > Math.min(space.bottom, listRect.bottom) - 10 || selRect.bottom < Math.max(space.top, listRect.top) + 10)
|
||
return null;
|
||
return this.view.state.facet(completionConfig).positionInfo(this.view, listRect, selRect, infoRect, space);
|
||
}
|
||
placeInfo(pos) {
|
||
if (this.info) {
|
||
if (pos) {
|
||
if (pos.style)
|
||
this.info.style.cssText = pos.style;
|
||
this.info.className = "cm-tooltip cm-completionInfo " + (pos.class || "");
|
||
} else {
|
||
this.info.style.cssText = "top: -1e6px";
|
||
}
|
||
}
|
||
}
|
||
createListBox(options, id, range) {
|
||
const ul = document.createElement("ul");
|
||
ul.id = id;
|
||
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], { 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");
|
||
let cls = this.optionClass(completion);
|
||
if (cls)
|
||
li.className = cls;
|
||
for (let source of this.optionContent) {
|
||
let node = source(completion, this.view.state, match);
|
||
if (node)
|
||
li.appendChild(node);
|
||
}
|
||
}
|
||
if (range.from)
|
||
ul.classList.add("cm-completionListIncompleteTop");
|
||
if (range.to < options.length)
|
||
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();
|
||
}
|
||
};
|
||
function completionTooltip(stateField, applyCompletion2) {
|
||
return (view) => new CompletionTooltip(view, stateField, applyCompletion2);
|
||
}
|
||
function scrollIntoView(container, element) {
|
||
let parent = container.getBoundingClientRect();
|
||
let self = element.getBoundingClientRect();
|
||
if (self.top < parent.top)
|
||
container.scrollTop -= parent.top - self.top;
|
||
else if (self.bottom > parent.bottom)
|
||
container.scrollTop += self.bottom - parent.bottom;
|
||
}
|
||
function score(option) {
|
||
return (option.boost || 0) * 100 + (option.apply ? 10 : 0) + (option.info ? 5 : 0) + (option.type ? 1 : 0);
|
||
}
|
||
function sortOptions(active, state) {
|
||
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) {
|
||
for (let option of a.result.options) {
|
||
addOption(new Option(option, a.source, getMatch ? getMatch(option) : [], 1e9 - options.length));
|
||
}
|
||
} else {
|
||
let matcher = new FuzzyMatcher(state.sliceDoc(a.from, a.to));
|
||
for (let option of a.result.options)
|
||
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 = /* @__PURE__ */ 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.score - a.score || compare(a.completion, b.completion))) {
|
||
let cur2 = opt.completion;
|
||
if (!prev || prev.label != cur2.label || prev.detail != cur2.detail || prev.type != null && cur2.type != null && prev.type != cur2.type || prev.apply != cur2.apply || prev.boost != cur2.boost)
|
||
result.push(opt);
|
||
else if (score(opt.completion) > score(prev))
|
||
result[result.length - 1] = opt;
|
||
prev = opt.completion;
|
||
}
|
||
return result;
|
||
}
|
||
var CompletionDialog = class _CompletionDialog {
|
||
constructor(options, attrs, tooltip, timestamp, selected, disabled) {
|
||
this.options = options;
|
||
this.attrs = attrs;
|
||
this.tooltip = tooltip;
|
||
this.timestamp = timestamp;
|
||
this.selected = selected;
|
||
this.disabled = disabled;
|
||
}
|
||
setSelected(selected, id) {
|
||
return selected == this.selected || selected >= this.options.length ? this : new _CompletionDialog(this.options, makeAttrs(id, selected), this.tooltip, this.timestamp, selected, this.disabled);
|
||
}
|
||
static build(active, state, id, prev, conf) {
|
||
let options = sortOptions(active, state);
|
||
if (!options.length) {
|
||
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;
|
||
if (prev && prev.selected != selected && prev.selected != -1) {
|
||
let selectedValue = prev.options[prev.selected].completion;
|
||
for (let i = 0; i < options.length; i++)
|
||
if (options[i].completion == selectedValue) {
|
||
selected = i;
|
||
break;
|
||
}
|
||
}
|
||
return new _CompletionDialog(options, makeAttrs(id, selected), {
|
||
pos: active.reduce((a, b) => b.hasResult() ? Math.min(a, b.from) : a, 1e8),
|
||
create: completionTooltip(completionState, applyCompletion),
|
||
above: conf.aboveCursor
|
||
}, prev ? prev.timestamp : Date.now(), selected, false);
|
||
}
|
||
map(changes) {
|
||
return new _CompletionDialog(this.options, this.attrs, Object.assign(Object.assign({}, this.tooltip), { pos: changes.mapPos(this.tooltip.pos) }), this.timestamp, this.selected, this.disabled);
|
||
}
|
||
};
|
||
var CompletionState = class _CompletionState {
|
||
constructor(active, id, open) {
|
||
this.active = active;
|
||
this.id = id;
|
||
this.open = open;
|
||
}
|
||
static start() {
|
||
return new _CompletionState(none, "cm-ac-" + Math.floor(Math.random() * 2e6).toString(36), null);
|
||
}
|
||
update(tr) {
|
||
let { state } = tr, conf = state.facet(completionConfig);
|
||
let sources = conf.override || 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
|
||
/* Inactive */
|
||
) ? 1 : 0
|
||
/* Inactive */
|
||
);
|
||
return value.update(tr, conf);
|
||
});
|
||
if (active.length == this.active.length && active.every((a, i) => a == this.active[i]))
|
||
active = this.active;
|
||
let open = this.open;
|
||
if (open && tr.docChanged)
|
||
open = open.map(tr.changes);
|
||
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
|
||
/* Pending */
|
||
))
|
||
open = null;
|
||
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);
|
||
return active == this.active && open == this.open ? this : new _CompletionState(active, this.id, open);
|
||
}
|
||
get tooltip() {
|
||
return this.open ? this.open.tooltip : null;
|
||
}
|
||
get attrs() {
|
||
return this.open ? this.open.attrs : baseAttrs;
|
||
}
|
||
};
|
||
function sameResults(a, b) {
|
||
if (a == b)
|
||
return true;
|
||
for (let iA = 0, iB = 0; ; ) {
|
||
while (iA < a.length && !a[iA].hasResult)
|
||
iA++;
|
||
while (iB < b.length && !b[iB].hasResult)
|
||
iB++;
|
||
let endA = iA == a.length, endB = iB == b.length;
|
||
if (endA || endB)
|
||
return endA == endB;
|
||
if (a[iA++].result != b[iB++].result)
|
||
return false;
|
||
}
|
||
}
|
||
var baseAttrs = {
|
||
"aria-autocomplete": "list"
|
||
};
|
||
function makeAttrs(id, selected) {
|
||
let result = {
|
||
"aria-autocomplete": "list",
|
||
"aria-haspopup": "listbox",
|
||
"aria-controls": id
|
||
};
|
||
if (selected > -1)
|
||
result["aria-activedescendant"] = id + "-" + selected;
|
||
return result;
|
||
}
|
||
var none = [];
|
||
function getUserEvent(tr) {
|
||
return tr.isUserEvent("input.type") ? "input" : tr.isUserEvent("delete.backward") ? "delete" : null;
|
||
}
|
||
var ActiveSource = class _ActiveSource {
|
||
constructor(source, state, explicitPos = -1) {
|
||
this.source = source;
|
||
this.state = state;
|
||
this.explicitPos = explicitPos;
|
||
}
|
||
hasResult() {
|
||
return false;
|
||
}
|
||
update(tr, conf) {
|
||
let event = getUserEvent(tr), value = this;
|
||
if (event)
|
||
value = value.handleUserEvent(tr, event, conf);
|
||
else if (tr.docChanged)
|
||
value = value.handleChange(tr);
|
||
else if (tr.selection && value.state != 0)
|
||
value = new _ActiveSource(
|
||
value.source,
|
||
0
|
||
/* Inactive */
|
||
);
|
||
for (let effect of tr.effects) {
|
||
if (effect.is(startCompletionEffect))
|
||
value = new _ActiveSource(value.source, 1, effect.value ? cur(tr.state) : -1);
|
||
else if (effect.is(closeCompletionEffect))
|
||
value = new _ActiveSource(
|
||
value.source,
|
||
0
|
||
/* Inactive */
|
||
);
|
||
else if (effect.is(setActiveEffect)) {
|
||
for (let active of effect.value)
|
||
if (active.source == value.source)
|
||
value = active;
|
||
}
|
||
}
|
||
return value;
|
||
}
|
||
handleUserEvent(tr, type, conf) {
|
||
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
|
||
/* Inactive */
|
||
) : this.map(tr.changes);
|
||
}
|
||
map(changes) {
|
||
return changes.empty || this.explicitPos < 0 ? this : new _ActiveSource(this.source, this.state, changes.mapPos(this.explicitPos));
|
||
}
|
||
};
|
||
var ActiveResult = class _ActiveResult extends ActiveSource {
|
||
constructor(source, explicitPos, result, from, to) {
|
||
super(source, 2, explicitPos);
|
||
this.result = result;
|
||
this.from = from;
|
||
this.to = to;
|
||
}
|
||
hasResult() {
|
||
return true;
|
||
}
|
||
handleUserEvent(tr, type, conf) {
|
||
var _a;
|
||
let from = tr.changes.mapPos(this.from), to = tr.changes.mapPos(this.to, 1);
|
||
let pos = cur(tr.state);
|
||
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 : 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, explicitPos);
|
||
}
|
||
handleChange(tr) {
|
||
return tr.changes.touchesRange(this.from, this.to) ? new ActiveSource(
|
||
this.source,
|
||
0
|
||
/* Inactive */
|
||
) : this.map(tr.changes);
|
||
}
|
||
map(mapping) {
|
||
return mapping.empty ? this : new _ActiveResult(this.source, this.explicitPos < 0 ? -1 : mapping.mapPos(this.explicitPos), this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1));
|
||
}
|
||
};
|
||
function checkValid(validFor, state, from, to) {
|
||
if (!validFor)
|
||
return false;
|
||
let text = state.sliceDoc(from, to);
|
||
return typeof validFor == "function" ? validFor(text, from, to, state) : ensureAnchor(validFor, true).test(text);
|
||
}
|
||
var setActiveEffect = StateEffect.define({
|
||
map(sources, mapping) {
|
||
return sources.map((s) => s.map(mapping));
|
||
}
|
||
});
|
||
var setSelectedEffect = StateEffect.define();
|
||
var completionState = StateField.define({
|
||
create() {
|
||
return CompletionState.start();
|
||
},
|
||
update(value, tr) {
|
||
return value.update(tr);
|
||
},
|
||
provide: (f) => [
|
||
showTooltip.from(f, (val) => val.tooltip),
|
||
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;
|
||
}
|
||
function moveCompletionSelection(forward, by = "option") {
|
||
return (view) => {
|
||
let cState = view.state.field(completionState, false);
|
||
if (!cState || !cState.open || cState.open.disabled || Date.now() - cState.open.timestamp < view.state.facet(completionConfig).interactionDelay)
|
||
return false;
|
||
let step = 1, tooltip;
|
||
if (by == "page" && (tooltip = getTooltip(view, cState.open.tooltip)))
|
||
step = Math.max(2, Math.floor(tooltip.dom.offsetHeight / tooltip.dom.querySelector("li").offsetHeight) - 1);
|
||
let { length } = cState.open.options;
|
||
let selected = cState.open.selected > -1 ? cState.open.selected + step * (forward ? 1 : -1) : forward ? 0 : length - 1;
|
||
if (selected < 0)
|
||
selected = by == "page" ? 0 : length - 1;
|
||
else if (selected >= length)
|
||
selected = by == "page" ? length - 1 : 0;
|
||
view.dispatch({ effects: setSelectedEffect.of(selected) });
|
||
return true;
|
||
};
|
||
}
|
||
var acceptCompletion = (view) => {
|
||
let cState = view.state.field(completionState, false);
|
||
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;
|
||
return applyCompletion(view, cState.open.options[cState.open.selected]);
|
||
};
|
||
var startCompletion = (view) => {
|
||
let cState = view.state.field(completionState, false);
|
||
if (!cState)
|
||
return false;
|
||
view.dispatch({ effects: startCompletionEffect.of(true) });
|
||
return true;
|
||
};
|
||
var closeCompletion = (view) => {
|
||
let cState = view.state.field(completionState, false);
|
||
if (!cState || !cState.active.some(
|
||
(a) => a.state != 0
|
||
/* Inactive */
|
||
))
|
||
return false;
|
||
view.dispatch({ effects: closeCompletionEffect.of(null) });
|
||
return true;
|
||
};
|
||
var RunningQuery = class {
|
||
constructor(active, context) {
|
||
this.active = active;
|
||
this.context = context;
|
||
this.time = Date.now();
|
||
this.updates = [];
|
||
this.done = void 0;
|
||
}
|
||
};
|
||
var DebounceTime = 50;
|
||
var MaxUpdateCount = 50;
|
||
var MinAbortTime = 1e3;
|
||
var completionPlugin = ViewPlugin.fromClass(class {
|
||
constructor(view) {
|
||
this.view = view;
|
||
this.debounceUpdate = -1;
|
||
this.running = [];
|
||
this.debounceAccept = -1;
|
||
this.composing = 0;
|
||
for (let active of view.state.field(completionState).active)
|
||
if (active.state == 1)
|
||
this.startQuery(active);
|
||
}
|
||
update(update) {
|
||
let cState = update.state.field(completionState);
|
||
if (!update.selectionSet && !update.docChanged && update.startState.field(completionState) == cState)
|
||
return;
|
||
let doesReset = update.transactions.some((tr) => {
|
||
return (tr.selection || tr.docChanged) && !getUserEvent(tr);
|
||
});
|
||
for (let i = 0; i < this.running.length; i++) {
|
||
let query = this.running[i];
|
||
if (doesReset || query.updates.length + update.transactions.length > MaxUpdateCount && Date.now() - query.time > MinAbortTime) {
|
||
for (let handler of query.context.abortListeners) {
|
||
try {
|
||
handler();
|
||
} catch (e) {
|
||
logException(this.view.state, e);
|
||
}
|
||
}
|
||
query.context.abortListeners = null;
|
||
this.running.splice(i--, 1);
|
||
} else {
|
||
query.updates.push(...update.transactions);
|
||
}
|
||
}
|
||
if (this.debounceUpdate > -1)
|
||
clearTimeout(this.debounceUpdate);
|
||
this.debounceUpdate = cState.active.some((a) => a.state == 1 && !this.running.some((q) => q.active.source == a.source)) ? setTimeout(() => this.startUpdate(), DebounceTime) : -1;
|
||
if (this.composing != 0)
|
||
for (let tr of update.transactions) {
|
||
if (getUserEvent(tr) == "input")
|
||
this.composing = 2;
|
||
else if (this.composing == 2 && tr.selection)
|
||
this.composing = 3;
|
||
}
|
||
}
|
||
startUpdate() {
|
||
this.debounceUpdate = -1;
|
||
let { state } = this.view, cState = state.field(completionState);
|
||
for (let active of cState.active) {
|
||
if (active.state == 1 && !this.running.some((r) => r.active.source == active.source))
|
||
this.startQuery(active);
|
||
}
|
||
}
|
||
startQuery(active) {
|
||
let { state } = this.view, pos = cur(state);
|
||
let context = new CompletionContext(state, pos, active.explicitPos == pos);
|
||
let pending = new RunningQuery(active, context);
|
||
this.running.push(pending);
|
||
Promise.resolve(active.source(context)).then((result) => {
|
||
if (!pending.context.aborted) {
|
||
pending.done = result || null;
|
||
this.scheduleAccept();
|
||
}
|
||
}, (err) => {
|
||
this.view.dispatch({ effects: closeCompletionEffect.of(null) });
|
||
logException(this.view.state, err);
|
||
});
|
||
}
|
||
scheduleAccept() {
|
||
if (this.running.every((q) => q.done !== void 0))
|
||
this.accept();
|
||
else if (this.debounceAccept < 0)
|
||
this.debounceAccept = setTimeout(() => this.accept(), DebounceTime);
|
||
}
|
||
// For each finished query in this.running, try to create a result
|
||
// or, if appropriate, restart the query.
|
||
accept() {
|
||
var _a;
|
||
if (this.debounceAccept > -1)
|
||
clearTimeout(this.debounceAccept);
|
||
this.debounceAccept = -1;
|
||
let updated = [];
|
||
let conf = this.view.state.facet(completionConfig);
|
||
for (let i = 0; i < this.running.length; i++) {
|
||
let query = this.running[i];
|
||
if (query.done === void 0)
|
||
continue;
|
||
this.running.splice(i--, 1);
|
||
if (query.done) {
|
||
let active = new ActiveResult(query.active.source, query.active.explicitPos, query.done, query.done.from, (_a = query.done.to) !== null && _a !== void 0 ? _a : cur(query.updates.length ? query.updates[0].startState : this.view.state));
|
||
for (let tr of query.updates)
|
||
active = active.update(tr, conf);
|
||
if (active.hasResult()) {
|
||
updated.push(active);
|
||
continue;
|
||
}
|
||
}
|
||
let current = this.view.state.field(completionState).active.find((a) => a.source == query.active.source);
|
||
if (current && current.state == 1) {
|
||
if (query.done == null) {
|
||
let active = new ActiveSource(
|
||
query.active.source,
|
||
0
|
||
/* Inactive */
|
||
);
|
||
for (let tr of query.updates)
|
||
active = active.update(tr, conf);
|
||
if (active.state != 1)
|
||
updated.push(active);
|
||
} else {
|
||
this.startQuery(current);
|
||
}
|
||
}
|
||
}
|
||
if (updated.length)
|
||
this.view.dispatch({ effects: setActiveEffect.of(updated) });
|
||
}
|
||
}, {
|
||
eventHandlers: {
|
||
blur(event) {
|
||
let state = this.view.state.field(completionState, false);
|
||
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;
|
||
},
|
||
compositionend() {
|
||
if (this.composing == 3) {
|
||
setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
|
||
}
|
||
this.composing = 0;
|
||
}
|
||
}
|
||
});
|
||
var baseTheme = EditorView.baseTheme({
|
||
".cm-tooltip.cm-tooltip-autocomplete": {
|
||
"& > ul": {
|
||
fontFamily: "monospace",
|
||
whiteSpace: "nowrap",
|
||
overflow: "hidden auto",
|
||
maxWidth_fallback: "700px",
|
||
maxWidth: "min(700px, 95vw)",
|
||
minWidth: "250px",
|
||
maxHeight: "10em",
|
||
height: "100%",
|
||
listStyle: "none",
|
||
margin: 0,
|
||
padding: 0,
|
||
"& > 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]": {
|
||
background: "#17c",
|
||
color: "white"
|
||
},
|
||
"&light .cm-tooltip-autocomplete-disabled ul li[aria-selected]": {
|
||
background: "#777"
|
||
},
|
||
"&dark .cm-tooltip-autocomplete ul li[aria-selected]": {
|
||
background: "#347",
|
||
color: "white"
|
||
},
|
||
"&dark .cm-tooltip-autocomplete-disabled ul li[aria-selected]": {
|
||
background: "#444"
|
||
},
|
||
".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after": {
|
||
content: '"···"',
|
||
opacity: 0.5,
|
||
display: "block",
|
||
textAlign: "center"
|
||
},
|
||
".cm-tooltip.cm-completionInfo": {
|
||
position: "absolute",
|
||
padding: "3px 9px",
|
||
width: "max-content",
|
||
maxWidth: `${400}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}px` },
|
||
".cm-completionInfo.cm-completionInfo-right-narrow": { left: `${30}px` },
|
||
"&light .cm-snippetField": { backgroundColor: "#00000022" },
|
||
"&dark .cm-snippetField": { backgroundColor: "#ffffff22" },
|
||
".cm-snippetFieldPosition": {
|
||
verticalAlign: "text-top",
|
||
width: 0,
|
||
height: "1.15em",
|
||
display: "inline-block",
|
||
margin: "0 -0.7px -.7em",
|
||
borderLeft: "1.4px dotted #888"
|
||
},
|
||
".cm-completionMatchedText": {
|
||
textDecoration: "underline"
|
||
},
|
||
".cm-completionDetail": {
|
||
marginLeft: "0.5em",
|
||
fontStyle: "italic"
|
||
},
|
||
".cm-completionIcon": {
|
||
fontSize: "90%",
|
||
width: ".8em",
|
||
display: "inline-block",
|
||
textAlign: "center",
|
||
paddingRight: ".6em",
|
||
opacity: "0.6",
|
||
boxSizing: "content-box"
|
||
},
|
||
".cm-completionIcon-function, .cm-completionIcon-method": {
|
||
"&:after": { content: "'ƒ'" }
|
||
},
|
||
".cm-completionIcon-class": {
|
||
"&:after": { content: "'○'" }
|
||
},
|
||
".cm-completionIcon-interface": {
|
||
"&:after": { content: "'◌'" }
|
||
},
|
||
".cm-completionIcon-variable": {
|
||
"&:after": { content: "'𝑥'" }
|
||
},
|
||
".cm-completionIcon-constant": {
|
||
"&:after": { content: "'𝐶'" }
|
||
},
|
||
".cm-completionIcon-type": {
|
||
"&:after": { content: "'𝑡'" }
|
||
},
|
||
".cm-completionIcon-enum": {
|
||
"&:after": { content: "'∪'" }
|
||
},
|
||
".cm-completionIcon-property": {
|
||
"&:after": { content: "'□'" }
|
||
},
|
||
".cm-completionIcon-keyword": {
|
||
"&:after": { content: "'🔑︎'" }
|
||
// Disable emoji rendering
|
||
},
|
||
".cm-completionIcon-namespace": {
|
||
"&:after": { content: "'▢'" }
|
||
},
|
||
".cm-completionIcon-text": {
|
||
"&:after": { content: "'abc'", fontSize: "50%", verticalAlign: "middle" }
|
||
}
|
||
});
|
||
var FieldPos = class {
|
||
constructor(field, line, from, to) {
|
||
this.field = field;
|
||
this.line = line;
|
||
this.from = from;
|
||
this.to = to;
|
||
}
|
||
};
|
||
var FieldRange = class _FieldRange {
|
||
constructor(field, from, to) {
|
||
this.field = field;
|
||
this.from = from;
|
||
this.to = to;
|
||
}
|
||
map(changes) {
|
||
let from = changes.mapPos(this.from, -1, MapMode.TrackDel);
|
||
let to = changes.mapPos(this.to, 1, MapMode.TrackDel);
|
||
return from == null || to == null ? null : new _FieldRange(this.field, from, to);
|
||
}
|
||
};
|
||
var Snippet = class _Snippet {
|
||
constructor(lines, fieldPositions) {
|
||
this.lines = lines;
|
||
this.fieldPositions = fieldPositions;
|
||
}
|
||
instantiate(state, pos) {
|
||
let text = [], lineStart = [pos];
|
||
let lineObj = state.doc.lineAt(pos), baseIndent = /^\s*/.exec(lineObj.text)[0];
|
||
for (let line of this.lines) {
|
||
if (text.length) {
|
||
let indent = baseIndent, tabs = /^\t*/.exec(line)[0].length;
|
||
for (let i = 0; i < tabs; i++)
|
||
indent += state.facet(indentUnit);
|
||
lineStart.push(pos + indent.length - tabs);
|
||
line = indent + line.slice(tabs);
|
||
}
|
||
text.push(line);
|
||
pos += line.length + 1;
|
||
}
|
||
let ranges = this.fieldPositions.map((pos2) => new FieldRange(pos2.field, lineStart[pos2.line] + pos2.from, lineStart[pos2.line] + pos2.to));
|
||
return { text, ranges };
|
||
}
|
||
static parse(template) {
|
||
let fields = [];
|
||
let lines = [], positions = [], m;
|
||
for (let line of template.split(/\r\n?|\n/)) {
|
||
while (m = /[#$]\{(?:(\d+)(?::([^}]*))?|([^}]*))\}/.exec(line)) {
|
||
let seq = m[1] ? +m[1] : null, name = m[2] || m[3] || "", found = -1;
|
||
for (let i = 0; i < fields.length; i++) {
|
||
if (seq != null ? fields[i].seq == seq : name ? fields[i].name == name : false)
|
||
found = i;
|
||
}
|
||
if (found < 0) {
|
||
let i = 0;
|
||
while (i < fields.length && (seq == null || fields[i].seq != null && fields[i].seq < seq))
|
||
i++;
|
||
fields.splice(i, 0, { seq, name });
|
||
found = i;
|
||
for (let pos of positions)
|
||
if (pos.field >= found)
|
||
pos.field++;
|
||
}
|
||
positions.push(new FieldPos(found, lines.length, m.index, m.index + name.length));
|
||
line = line.slice(0, m.index) + name + line.slice(m.index + m[0].length);
|
||
}
|
||
for (let esc; esc = /\\([{}])/.exec(line); ) {
|
||
line = line.slice(0, esc.index) + esc[1] + line.slice(esc.index + esc[0].length);
|
||
for (let pos of positions)
|
||
if (pos.line == lines.length && pos.from > esc.index) {
|
||
pos.from--;
|
||
pos.to--;
|
||
}
|
||
}
|
||
lines.push(line);
|
||
}
|
||
return new _Snippet(lines, positions);
|
||
}
|
||
};
|
||
var fieldMarker = Decoration.widget({ widget: new class extends WidgetType {
|
||
toDOM() {
|
||
let span = document.createElement("span");
|
||
span.className = "cm-snippetFieldPosition";
|
||
return span;
|
||
}
|
||
ignoreEvent() {
|
||
return false;
|
||
}
|
||
}() });
|
||
var fieldRange = Decoration.mark({ class: "cm-snippetField" });
|
||
var ActiveSnippet = class _ActiveSnippet {
|
||
constructor(ranges, active) {
|
||
this.ranges = ranges;
|
||
this.active = active;
|
||
this.deco = Decoration.set(ranges.map((r) => (r.from == r.to ? fieldMarker : fieldRange).range(r.from, r.to)));
|
||
}
|
||
map(changes) {
|
||
let ranges = [];
|
||
for (let r of this.ranges) {
|
||
let mapped = r.map(changes);
|
||
if (!mapped)
|
||
return null;
|
||
ranges.push(mapped);
|
||
}
|
||
return new _ActiveSnippet(ranges, this.active);
|
||
}
|
||
selectionInsideField(sel) {
|
||
return sel.ranges.every((range) => this.ranges.some((r) => r.field == this.active && r.from <= range.from && r.to >= range.to));
|
||
}
|
||
};
|
||
var setActive = StateEffect.define({
|
||
map(value, changes) {
|
||
return value && value.map(changes);
|
||
}
|
||
});
|
||
var moveToField = StateEffect.define();
|
||
var snippetState = StateField.define({
|
||
create() {
|
||
return null;
|
||
},
|
||
update(value, tr) {
|
||
for (let effect of tr.effects) {
|
||
if (effect.is(setActive))
|
||
return effect.value;
|
||
if (effect.is(moveToField) && value)
|
||
return new ActiveSnippet(value.ranges, effect.value);
|
||
}
|
||
if (value && tr.docChanged)
|
||
value = value.map(tr.changes);
|
||
if (value && tr.selection && !value.selectionInsideField(tr.selection))
|
||
value = null;
|
||
return value;
|
||
},
|
||
provide: (f) => EditorView.decorations.from(f, (val) => val ? val.deco : Decoration.none)
|
||
});
|
||
function fieldSelection(ranges, field) {
|
||
return EditorSelection.create(ranges.filter((r) => r.field == field).map((r) => EditorSelection.range(r.from, r.to)));
|
||
}
|
||
function snippet(template) {
|
||
let snippet2 = Snippet.parse(template);
|
||
return (editor, completion, from, to) => {
|
||
let { text, ranges } = snippet2.instantiate(editor.state, from);
|
||
let spec = {
|
||
changes: { from, to, insert: Text.of(text) },
|
||
scrollIntoView: true,
|
||
annotations: completion ? pickedCompletion.of(completion) : void 0
|
||
};
|
||
if (ranges.length)
|
||
spec.selection = fieldSelection(ranges, 0);
|
||
if (ranges.length > 1) {
|
||
let active = new ActiveSnippet(ranges, 0);
|
||
let effects = spec.effects = [setActive.of(active)];
|
||
if (editor.state.field(snippetState, false) === void 0)
|
||
effects.push(StateEffect.appendConfig.of([snippetState, addSnippetKeymap, snippetPointerHandler, baseTheme]));
|
||
}
|
||
editor.dispatch(editor.state.update(spec));
|
||
};
|
||
}
|
||
function moveField(dir) {
|
||
return ({ state, dispatch }) => {
|
||
let active = state.field(snippetState, false);
|
||
if (!active || dir < 0 && active.active == 0)
|
||
return false;
|
||
let next = active.active + dir, last = dir > 0 && !active.ranges.some((r) => r.field == next + dir);
|
||
dispatch(state.update({
|
||
selection: fieldSelection(active.ranges, next),
|
||
effects: setActive.of(last ? null : new ActiveSnippet(active.ranges, next))
|
||
}));
|
||
return true;
|
||
};
|
||
}
|
||
var clearSnippet = ({ state, dispatch }) => {
|
||
let active = state.field(snippetState, false);
|
||
if (!active)
|
||
return false;
|
||
dispatch(state.update({ effects: setActive.of(null) }));
|
||
return true;
|
||
};
|
||
var nextSnippetField = moveField(1);
|
||
var prevSnippetField = moveField(-1);
|
||
function hasNextSnippetField(state) {
|
||
let active = state.field(snippetState, false);
|
||
return !!(active && active.ranges.some((r) => r.field == active.active + 1));
|
||
}
|
||
function hasPrevSnippetField(state) {
|
||
let active = state.field(snippetState, false);
|
||
return !!(active && active.active > 0);
|
||
}
|
||
var defaultSnippetKeymap = [
|
||
{ key: "Tab", run: nextSnippetField, shift: prevSnippetField },
|
||
{ key: "Escape", run: clearSnippet }
|
||
];
|
||
var snippetKeymap = Facet.define({
|
||
combine(maps) {
|
||
return maps.length ? maps[0] : defaultSnippetKeymap;
|
||
}
|
||
});
|
||
var addSnippetKeymap = Prec.highest(keymap.compute([snippetKeymap], (state) => state.facet(snippetKeymap)));
|
||
function snippetCompletion(template, completion) {
|
||
return Object.assign(Object.assign({}, completion), { apply: snippet(template) });
|
||
}
|
||
var snippetPointerHandler = EditorView.domEventHandlers({
|
||
mousedown(event, view) {
|
||
let active = view.state.field(snippetState, false), pos;
|
||
if (!active || (pos = view.posAtCoords({ x: event.clientX, y: event.clientY })) == null)
|
||
return false;
|
||
let match = active.ranges.find((r) => r.from <= pos && r.to >= pos);
|
||
if (!match || match.field == active.active)
|
||
return false;
|
||
view.dispatch({
|
||
selection: fieldSelection(active.ranges, match.field),
|
||
effects: setActive.of(active.ranges.some((r) => r.field > match.field) ? new ActiveSnippet(active.ranges, match.field) : null)
|
||
});
|
||
return true;
|
||
}
|
||
});
|
||
function wordRE(wordChars) {
|
||
let escaped = wordChars.replace(/[\\[.+*?(){|^$]/g, "\\$&");
|
||
try {
|
||
return new RegExp(`[\\p{Alphabetic}\\p{Number}_${escaped}]+`, "ug");
|
||
} catch (_a) {
|
||
return new RegExp(`[w${escaped}]`, "g");
|
||
}
|
||
}
|
||
function mapRE(re, f) {
|
||
return new RegExp(f(re.source), re.unicode ? "u" : "");
|
||
}
|
||
var wordCaches = /* @__PURE__ */ Object.create(null);
|
||
function wordCache(wordChars) {
|
||
return wordCaches[wordChars] || (wordCaches[wordChars] = /* @__PURE__ */ new WeakMap());
|
||
}
|
||
function storeWords(doc, wordRE2, result, seen, ignoreAt) {
|
||
for (let lines = doc.iterLines(), pos = 0; !lines.next().done; ) {
|
||
let { value } = lines, m;
|
||
wordRE2.lastIndex = 0;
|
||
while (m = wordRE2.exec(value)) {
|
||
if (!seen[m[0]] && pos + m.index != ignoreAt) {
|
||
result.push({ type: "text", label: m[0] });
|
||
seen[m[0]] = true;
|
||
if (result.length >= 2e3)
|
||
return;
|
||
}
|
||
}
|
||
pos += value.length + 1;
|
||
}
|
||
}
|
||
function collectWords(doc, cache, wordRE2, to, ignoreAt) {
|
||
let big = doc.length >= 1e3;
|
||
let cached = big && cache.get(doc);
|
||
if (cached)
|
||
return cached;
|
||
let result = [], seen = /* @__PURE__ */ Object.create(null);
|
||
if (doc.children) {
|
||
let pos = 0;
|
||
for (let ch of doc.children) {
|
||
if (ch.length >= 1e3) {
|
||
for (let c of collectWords(ch, cache, wordRE2, to - pos, ignoreAt - pos)) {
|
||
if (!seen[c.label]) {
|
||
seen[c.label] = true;
|
||
result.push(c);
|
||
}
|
||
}
|
||
} else {
|
||
storeWords(ch, wordRE2, result, seen, ignoreAt - pos);
|
||
}
|
||
pos += ch.length + 1;
|
||
}
|
||
} else {
|
||
storeWords(doc, wordRE2, result, seen, ignoreAt);
|
||
}
|
||
if (big && result.length < 2e3)
|
||
cache.set(doc, result);
|
||
return result;
|
||
}
|
||
var completeAnyWord = (context) => {
|
||
let wordChars = context.state.languageDataAt("wordChars", context.pos).join("");
|
||
let re = wordRE(wordChars);
|
||
let token = context.matchBefore(mapRE(re, (s) => s + "$"));
|
||
if (!token && !context.explicit)
|
||
return null;
|
||
let from = token ? token.from : context.pos;
|
||
let options = collectWords(context.state.doc, wordCache(wordChars), re, 5e4, from);
|
||
return { from, options, validFor: mapRE(re, (s) => "^" + s) };
|
||
};
|
||
var defaults = {
|
||
brackets: ["(", "[", "{", "'", '"'],
|
||
before: ")]}:;>",
|
||
stringPrefixes: []
|
||
};
|
||
var closeBracketEffect = StateEffect.define({
|
||
map(value, mapping) {
|
||
let mapped = mapping.mapPos(value, -1, MapMode.TrackAfter);
|
||
return mapped == null ? void 0 : mapped;
|
||
}
|
||
});
|
||
var closedBracket = new class extends RangeValue {
|
||
}();
|
||
closedBracket.startSide = 1;
|
||
closedBracket.endSide = -1;
|
||
var bracketState = StateField.define({
|
||
create() {
|
||
return RangeSet.empty;
|
||
},
|
||
update(value, tr) {
|
||
if (tr.selection) {
|
||
let lineStart = tr.state.doc.lineAt(tr.selection.main.head).from;
|
||
let prevLineStart = tr.startState.doc.lineAt(tr.startState.selection.main.head).from;
|
||
if (lineStart != tr.changes.mapPos(prevLineStart, -1))
|
||
value = RangeSet.empty;
|
||
}
|
||
value = value.map(tr.changes);
|
||
for (let effect of tr.effects)
|
||
if (effect.is(closeBracketEffect))
|
||
value = value.update({ add: [closedBracket.range(effect.value, effect.value + 1)] });
|
||
return value;
|
||
}
|
||
});
|
||
function closeBrackets() {
|
||
return [inputHandler, bracketState];
|
||
}
|
||
var definedClosing = "()[]{}<>";
|
||
function closing(ch) {
|
||
for (let i = 0; i < definedClosing.length; i += 2)
|
||
if (definedClosing.charCodeAt(i) == ch)
|
||
return definedClosing.charAt(i + 1);
|
||
return fromCodePoint(ch < 128 ? ch : ch + 1);
|
||
}
|
||
function config(state, pos) {
|
||
return state.languageDataAt("closeBrackets", pos)[0] || defaults;
|
||
}
|
||
var android = typeof navigator == "object" && /Android\b/.test(navigator.userAgent);
|
||
var inputHandler = EditorView.inputHandler.of((view, from, to, insert) => {
|
||
if ((android ? view.composing : view.compositionStarted) || view.state.readOnly)
|
||
return false;
|
||
let sel = view.state.selection.main;
|
||
if (insert.length > 2 || insert.length == 2 && codePointSize(codePointAt(insert, 0)) == 1 || from != sel.from || to != sel.to)
|
||
return false;
|
||
let tr = insertBracket(view.state, insert);
|
||
if (!tr)
|
||
return false;
|
||
view.dispatch(tr);
|
||
return true;
|
||
});
|
||
var deleteBracketPair = ({ state, dispatch }) => {
|
||
if (state.readOnly)
|
||
return false;
|
||
let conf = config(state, state.selection.main.head);
|
||
let tokens = conf.brackets || defaults.brackets;
|
||
let dont = null, changes = state.changeByRange((range) => {
|
||
if (range.empty) {
|
||
let before = prevChar(state.doc, range.head);
|
||
for (let token of tokens) {
|
||
if (token == before && nextChar(state.doc, range.head) == closing(codePointAt(token, 0)))
|
||
return {
|
||
changes: { from: range.head - token.length, to: range.head + token.length },
|
||
range: EditorSelection.cursor(range.head - token.length)
|
||
};
|
||
}
|
||
}
|
||
return { range: dont = range };
|
||
});
|
||
if (!dont)
|
||
dispatch(state.update(changes, { scrollIntoView: true, userEvent: "delete.backward" }));
|
||
return !dont;
|
||
};
|
||
var closeBracketsKeymap = [
|
||
{ key: "Backspace", run: deleteBracketPair }
|
||
];
|
||
function insertBracket(state, bracket) {
|
||
let conf = config(state, state.selection.main.head);
|
||
let tokens = conf.brackets || defaults.brackets;
|
||
for (let tok of tokens) {
|
||
let closed = closing(codePointAt(tok, 0));
|
||
if (bracket == tok)
|
||
return closed == tok ? handleSame(state, tok, tokens.indexOf(tok + tok + tok) > -1, conf) : handleOpen(state, tok, closed, conf.before || defaults.before);
|
||
if (bracket == closed && closedBracketAt(state, state.selection.main.from))
|
||
return handleClose(state, tok, closed);
|
||
}
|
||
return null;
|
||
}
|
||
function closedBracketAt(state, pos) {
|
||
let found = false;
|
||
state.field(bracketState).between(0, state.doc.length, (from) => {
|
||
if (from == pos)
|
||
found = true;
|
||
});
|
||
return found;
|
||
}
|
||
function nextChar(doc, pos) {
|
||
let next = doc.sliceString(pos, pos + 2);
|
||
return next.slice(0, codePointSize(codePointAt(next, 0)));
|
||
}
|
||
function prevChar(doc, pos) {
|
||
let prev = doc.sliceString(pos - 2, pos);
|
||
return codePointSize(codePointAt(prev, 0)) == prev.length ? prev : prev.slice(1);
|
||
}
|
||
function handleOpen(state, open, close, closeBefore) {
|
||
let dont = null, changes = state.changeByRange((range) => {
|
||
if (!range.empty)
|
||
return {
|
||
changes: [{ insert: open, from: range.from }, { insert: close, from: range.to }],
|
||
effects: closeBracketEffect.of(range.to + open.length),
|
||
range: EditorSelection.range(range.anchor + open.length, range.head + open.length)
|
||
};
|
||
let next = nextChar(state.doc, range.head);
|
||
if (!next || /\s/.test(next) || closeBefore.indexOf(next) > -1)
|
||
return {
|
||
changes: { insert: open + close, from: range.head },
|
||
effects: closeBracketEffect.of(range.head + open.length),
|
||
range: EditorSelection.cursor(range.head + open.length)
|
||
};
|
||
return { range: dont = range };
|
||
});
|
||
return dont ? null : state.update(changes, {
|
||
scrollIntoView: true,
|
||
userEvent: "input.type"
|
||
});
|
||
}
|
||
function handleClose(state, _open, close) {
|
||
let dont = null, changes = state.changeByRange((range) => {
|
||
if (range.empty && nextChar(state.doc, range.head) == close)
|
||
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(changes, {
|
||
scrollIntoView: true,
|
||
userEvent: "input.type"
|
||
});
|
||
}
|
||
function handleSame(state, token, allowTriple, config2) {
|
||
let stringPrefixes = config2.stringPrefixes || defaults.stringPrefixes;
|
||
let dont = null, changes = state.changeByRange((range) => {
|
||
if (!range.empty)
|
||
return {
|
||
changes: [{ insert: token, from: range.from }, { insert: token, from: range.to }],
|
||
effects: closeBracketEffect.of(range.to + token.length),
|
||
range: EditorSelection.range(range.anchor + token.length, range.head + token.length)
|
||
};
|
||
let pos = range.head, next = nextChar(state.doc, pos), start;
|
||
if (next == token) {
|
||
if (nodeStart(state, pos)) {
|
||
return {
|
||
changes: { insert: token + token, from: pos },
|
||
effects: closeBracketEffect.of(pos + token.length),
|
||
range: EditorSelection.cursor(pos + token.length)
|
||
};
|
||
} else if (closedBracketAt(state, pos)) {
|
||
let isTriple = allowTriple && state.sliceDoc(pos, pos + token.length * 3) == token + token + token;
|
||
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 && (start = canStartStringAt(state, pos - 2 * token.length, stringPrefixes)) > -1 && nodeStart(state, start)) {
|
||
return {
|
||
changes: { insert: token + token + token + token, from: pos },
|
||
effects: closeBracketEffect.of(pos + token.length),
|
||
range: EditorSelection.cursor(pos + token.length)
|
||
};
|
||
} else if (state.charCategorizer(pos)(next) != CharCategory.Word) {
|
||
if (canStartStringAt(state, pos, stringPrefixes) > -1 && !probablyInString(state, pos, token, stringPrefixes))
|
||
return {
|
||
changes: { insert: token + token, from: pos },
|
||
effects: closeBracketEffect.of(pos + token.length),
|
||
range: EditorSelection.cursor(pos + token.length)
|
||
};
|
||
}
|
||
return { range: dont = range };
|
||
});
|
||
return dont ? null : state.update(changes, {
|
||
scrollIntoView: true,
|
||
userEvent: "input.type"
|
||
});
|
||
}
|
||
function nodeStart(state, pos) {
|
||
let tree = syntaxTree(state).resolveInner(pos + 1);
|
||
return tree.parent && tree.from == pos;
|
||
}
|
||
function probablyInString(state, pos, quoteToken, prefixes) {
|
||
let node = syntaxTree(state).resolveInner(pos, -1);
|
||
let maxPrefix = prefixes.reduce((m, p) => Math.max(m, p.length), 0);
|
||
for (let i = 0; i < 5; i++) {
|
||
let start = state.sliceDoc(node.from, Math.min(node.to, node.from + quoteToken.length + maxPrefix));
|
||
let quotePos = start.indexOf(quoteToken);
|
||
if (!quotePos || quotePos > -1 && prefixes.indexOf(start.slice(0, quotePos)) > -1) {
|
||
let first = node.firstChild;
|
||
while (first && first.from == node.from && first.to - first.from > quoteToken.length + quotePos) {
|
||
if (state.sliceDoc(first.to - quoteToken.length, first.to) == quoteToken)
|
||
return false;
|
||
first = first.firstChild;
|
||
}
|
||
return true;
|
||
}
|
||
let parent = node.to == pos && node.parent;
|
||
if (!parent)
|
||
break;
|
||
node = parent;
|
||
}
|
||
return false;
|
||
}
|
||
function canStartStringAt(state, pos, prefixes) {
|
||
let charCat = state.charCategorizer(pos);
|
||
if (charCat(state.sliceDoc(pos - 1, pos)) != CharCategory.Word)
|
||
return pos;
|
||
for (let prefix of prefixes) {
|
||
let start = pos - prefix.length;
|
||
if (state.sliceDoc(start, pos) == prefix && charCat(state.sliceDoc(start - 1, start)) != CharCategory.Word)
|
||
return start;
|
||
}
|
||
return -1;
|
||
}
|
||
function autocompletion(config2 = {}) {
|
||
return [
|
||
completionState,
|
||
completionConfig.of(config2),
|
||
completionPlugin,
|
||
completionKeymapExt,
|
||
baseTheme
|
||
];
|
||
}
|
||
var completionKeymap = [
|
||
{ key: "Ctrl-Space", run: startCompletion },
|
||
{ key: "Escape", run: closeCompletion },
|
||
{ key: "ArrowDown", run: moveCompletionSelection(true) },
|
||
{ key: "ArrowUp", run: moveCompletionSelection(false) },
|
||
{ key: "PageDown", run: moveCompletionSelection(true, "page") },
|
||
{ key: "PageUp", run: moveCompletionSelection(false, "page") },
|
||
{ key: "Enter", run: acceptCompletion }
|
||
];
|
||
var completionKeymapExt = Prec.highest(keymap.computeN([completionConfig], (state) => state.facet(completionConfig).defaultKeymap ? [completionKeymap] : []));
|
||
function completionStatus(state) {
|
||
let cState = state.field(completionState, false);
|
||
return cState && cState.active.some(
|
||
(a) => a.state == 1
|
||
/* Pending */
|
||
) ? "pending" : cState && cState.active.some(
|
||
(a) => a.state != 0
|
||
/* Inactive */
|
||
) ? "active" : null;
|
||
}
|
||
var completionArrayCache = /* @__PURE__ */ new WeakMap();
|
||
function currentCompletions(state) {
|
||
var _a;
|
||
let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
|
||
if (!open || open.disabled)
|
||
return [];
|
||
let completions = completionArrayCache.get(open.options);
|
||
if (!completions)
|
||
completionArrayCache.set(open.options, completions = open.options.map((o) => o.completion));
|
||
return completions;
|
||
}
|
||
function selectedCompletion(state) {
|
||
var _a;
|
||
let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
|
||
return open && !open.disabled && open.selected >= 0 ? open.options[open.selected].completion : null;
|
||
}
|
||
function selectedCompletionIndex(state) {
|
||
var _a;
|
||
let open = (_a = state.field(completionState, false)) === null || _a === void 0 ? void 0 : _a.open;
|
||
return open && !open.disabled && open.selected >= 0 ? open.selected : null;
|
||
}
|
||
function setSelectedCompletion(index) {
|
||
return setSelectedEffect.of(index);
|
||
}
|
||
|
||
export {
|
||
CompletionContext,
|
||
completeFromList,
|
||
ifIn,
|
||
ifNotIn,
|
||
pickedCompletion,
|
||
insertCompletionText,
|
||
moveCompletionSelection,
|
||
acceptCompletion,
|
||
startCompletion,
|
||
closeCompletion,
|
||
snippet,
|
||
clearSnippet,
|
||
nextSnippetField,
|
||
prevSnippetField,
|
||
hasNextSnippetField,
|
||
hasPrevSnippetField,
|
||
snippetKeymap,
|
||
snippetCompletion,
|
||
completeAnyWord,
|
||
closeBrackets,
|
||
deleteBracketPair,
|
||
closeBracketsKeymap,
|
||
insertBracket,
|
||
autocompletion,
|
||
completionKeymap,
|
||
completionStatus,
|
||
currentCompletions,
|
||
selectedCompletion,
|
||
selectedCompletionIndex,
|
||
setSelectedCompletion
|
||
};
|
||
//# sourceMappingURL=chunk-UFI6XSZR.js.map
|