mirror of
https://github.com/dreamstarsky/runbin.git
synced 2026-05-15 22:33:09 +00:00
2514 lines
86 KiB
JavaScript
2514 lines
86 KiB
JavaScript
import {
|
||
syntaxTree
|
||
} from "./chunk-ELUT3ZLT.js";
|
||
import {
|
||
Decoration,
|
||
Direction,
|
||
EditorView,
|
||
GutterMarker,
|
||
ViewPlugin,
|
||
WidgetType,
|
||
getPanel,
|
||
getTooltip,
|
||
gutter,
|
||
hoverTooltip,
|
||
keymap,
|
||
logException,
|
||
showPanel,
|
||
showTooltip
|
||
} from "./chunk-HHQTB6RG.js";
|
||
import {
|
||
Annotation,
|
||
CharCategory,
|
||
EditorSelection,
|
||
Facet,
|
||
MapMode,
|
||
Prec,
|
||
RangeSet,
|
||
RangeSetBuilder,
|
||
RangeValue,
|
||
StateEffect,
|
||
StateField,
|
||
codePointAt,
|
||
codePointSize,
|
||
combineConfig,
|
||
fromCodePoint
|
||
} from "./chunk-JEVQZFNC.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, view) {
|
||
this.state = state;
|
||
this.pos = pos;
|
||
this.explicit = explicit;
|
||
this.view = view;
|
||
this.abortListeners = [];
|
||
this.abortOnDocChange = false;
|
||
}
|
||
/**
|
||
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).
|
||
|
||
By default, running queries will not be aborted for regular
|
||
typing or backspacing, on the assumption that they are likely to
|
||
return a result with a
|
||
[`validFor`](https://codemirror.net/6/docs/ref/#autocomplete.CompletionResult.validFor) field that
|
||
allows the result to be used after all. Passing `onDocChange:
|
||
true` will cause this query to be aborted for any document
|
||
change.
|
||
*/
|
||
addEventListener(type, listener, options) {
|
||
if (type == "abort" && this.abortListeners) {
|
||
this.abortListeners.push(listener);
|
||
if (options && options.onDocChange)
|
||
this.abortOnDocChange = true;
|
||
}
|
||
}
|
||
};
|
||
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;
|
||
};
|
||
}
|
||
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 };
|
||
let lines = state.toText(text);
|
||
return {
|
||
changes: { from: range.from + fromOff, to: to == main.from ? range.to : range.from + toOff, insert: lines },
|
||
range: EditorSelection.cursor(range.from + fromOff + lines.length)
|
||
};
|
||
})), { scrollIntoView: true, 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 this;
|
||
}
|
||
// 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 null;
|
||
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 null;
|
||
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 null;
|
||
}
|
||
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 ? null : 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 StrictMatcher = class {
|
||
constructor(pattern) {
|
||
this.pattern = pattern;
|
||
this.matched = [];
|
||
this.score = 0;
|
||
this.folded = pattern.toLowerCase();
|
||
}
|
||
match(word) {
|
||
if (word.length < this.pattern.length)
|
||
return null;
|
||
let start = word.slice(0, this.pattern.length);
|
||
let match = start == this.pattern ? 0 : start.toLowerCase() == this.folded ? -200 : null;
|
||
if (match == null)
|
||
return null;
|
||
this.matched = [0, start.length];
|
||
this.score = match + (word.length == this.pattern.length ? 0 : -100);
|
||
return this;
|
||
}
|
||
};
|
||
var completionConfig = Facet.define({
|
||
combine(configs) {
|
||
return combineConfig(configs, {
|
||
activateOnTyping: true,
|
||
activateOnCompletion: () => false,
|
||
activateOnTypingDelay: 100,
|
||
selectOnOpen: true,
|
||
override: null,
|
||
closeOnBlur: true,
|
||
maxRenderedOptions: 100,
|
||
defaultKeymap: true,
|
||
tooltipClass: () => "",
|
||
optionClass: () => "",
|
||
aboveCursor: false,
|
||
icons: true,
|
||
addToOptions: [],
|
||
positionInfo: defaultPositionInfo,
|
||
filterStrict: false,
|
||
compareCompletions: (a, b) => a.label.localeCompare(b.label),
|
||
interactionDelay: 75,
|
||
updateSyncTime: 100
|
||
}, {
|
||
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),
|
||
filterStrict: (a, b) => a || b
|
||
});
|
||
}
|
||
});
|
||
function joinClass(a, b) {
|
||
return a ? b ? a + " " + b : a : b;
|
||
}
|
||
function defaultPositionInfo(view, list, option, info, space, tooltip) {
|
||
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
|
||
/* Info.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;
|
||
}
|
||
}
|
||
let scaleY = (list.bottom - list.top) / tooltip.offsetHeight;
|
||
let scaleX = (list.right - list.left) / tooltip.offsetWidth;
|
||
return {
|
||
style: `${side}: ${offset / scaleY}px; max-width: ${maxWidth / scaleX}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, _v, 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) => {
|
||
let { options: options2 } = view.state.field(stateField).open;
|
||
for (let dom = e.target, match; dom && dom != this.dom; dom = dom.parentNode) {
|
||
if (dom.nodeName == "LI" && (match = /-(\d+)$/.exec(dom.id)) && +match[1] < options2.length) {
|
||
this.applyCompletion(view, options2[+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.showOptions(options, cState.id);
|
||
}
|
||
mount() {
|
||
this.updateSel();
|
||
}
|
||
showOptions(options, id) {
|
||
if (this.list)
|
||
this.list.remove();
|
||
this.list = this.dom.appendChild(this.createListBox(options, id, this.range));
|
||
this.list.addEventListener("scroll", () => {
|
||
if (this.info)
|
||
this.view.requestMeasure(this.placeInfoReq);
|
||
});
|
||
}
|
||
update(update) {
|
||
var _a;
|
||
let cState = update.state.field(this.stateField);
|
||
let prevState = update.startState.field(this.stateField);
|
||
this.updateTooltipClass(update.state);
|
||
if (cState != prevState) {
|
||
let { options, selected, disabled } = cState.open;
|
||
if (!prevState.open || prevState.open.options != options) {
|
||
this.range = rangeAroundSelected(options.length, selected, update.state.facet(completionConfig).maxRenderedOptions);
|
||
this.showOptions(options, cState.id);
|
||
}
|
||
this.updateSel();
|
||
if (disabled != ((_a = prevState.open) === null || _a === void 0 ? void 0 : _a.disabled))
|
||
this.dom.classList.toggle("cm-tooltip-autocomplete-disabled", !!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.showOptions(open.options, cState.id);
|
||
}
|
||
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 docElt = this.dom.ownerDocument.documentElement;
|
||
space = { left: 0, top: 0, right: docElt.clientWidth, bottom: docElt.clientHeight };
|
||
}
|
||
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, this.dom);
|
||
}
|
||
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"));
|
||
ul.addEventListener("mousedown", (e) => {
|
||
if (e.target == ul)
|
||
e.preventDefault();
|
||
});
|
||
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, this.view, 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();
|
||
let scaleY = parent.height / container.offsetHeight;
|
||
if (self.top < parent.top)
|
||
container.scrollTop -= (parent.top - self.top) / scaleY;
|
||
else if (self.bottom > parent.bottom)
|
||
container.scrollTop += (self.bottom - parent.bottom) / scaleY;
|
||
}
|
||
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);
|
||
}
|
||
};
|
||
let conf = state.facet(completionConfig);
|
||
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 pattern = state.sliceDoc(a.from, a.to), match;
|
||
let matcher = conf.filterStrict ? new StrictMatcher(pattern) : new FuzzyMatcher(pattern);
|
||
for (let option of a.result.options)
|
||
if (match = matcher.match(option.label)) {
|
||
let matched = !option.displayLabel ? match.matched : getMatch ? getMatch(option, match.matched) : [];
|
||
addOption(new Option(option, a.source, matched, match.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 = conf.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, didSetActive) {
|
||
if (prev && !didSetActive && active.some((s) => s.isPending))
|
||
return prev.setDisabled();
|
||
let options = sortOptions(active, state);
|
||
if (!options.length)
|
||
return prev && active.some((a) => a.isPending) ? prev.setDisabled() : 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: createTooltip,
|
||
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);
|
||
}
|
||
setDisabled() {
|
||
return new _CompletionDialog(this.options, this.attrs, this.tooltip, this.timestamp, this.selected, true);
|
||
}
|
||
};
|
||
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
|
||
/* State.Inactive */
|
||
) ? 1 : 0
|
||
/* State.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, didSet = tr.effects.some((e) => e.is(setActiveEffect));
|
||
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) || didSet)
|
||
open = CompletionDialog.build(active, state, this.id, open, conf, didSet);
|
||
else if (open && open.disabled && !active.some((a) => a.isPending))
|
||
open = null;
|
||
if (!open && active.every((a) => !a.isPending) && active.some((a) => a.hasResult()))
|
||
active = active.map((a) => a.hasResult() ? new ActiveSource(
|
||
a.source,
|
||
0
|
||
/* State.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 : this.active.length ? baseAttrs : noAttrs;
|
||
}
|
||
};
|
||
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"
|
||
};
|
||
var noAttrs = {};
|
||
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 getUpdateType(tr, conf) {
|
||
if (tr.isUserEvent("input.complete")) {
|
||
let completion = tr.annotation(pickedCompletion);
|
||
if (completion && conf.activateOnCompletion(completion))
|
||
return 4 | 8;
|
||
}
|
||
let typing = tr.isUserEvent("input.type");
|
||
return typing && conf.activateOnTyping ? 4 | 1 : typing ? 1 : tr.isUserEvent("delete.backward") ? 2 : tr.selection ? 8 : tr.docChanged ? 16 : 0;
|
||
}
|
||
var ActiveSource = class _ActiveSource {
|
||
constructor(source, state, explicit = false) {
|
||
this.source = source;
|
||
this.state = state;
|
||
this.explicit = explicit;
|
||
}
|
||
hasResult() {
|
||
return false;
|
||
}
|
||
get isPending() {
|
||
return this.state == 1;
|
||
}
|
||
update(tr, conf) {
|
||
let type = getUpdateType(tr, conf), value = this;
|
||
if (type & 8 || type & 16 && this.touches(tr))
|
||
value = new _ActiveSource(
|
||
value.source,
|
||
0
|
||
/* State.Inactive */
|
||
);
|
||
if (type & 4 && value.state == 0)
|
||
value = new _ActiveSource(
|
||
this.source,
|
||
1
|
||
/* State.Pending */
|
||
);
|
||
value = value.updateFor(tr, type);
|
||
for (let effect of tr.effects) {
|
||
if (effect.is(startCompletionEffect))
|
||
value = new _ActiveSource(value.source, 1, effect.value);
|
||
else if (effect.is(closeCompletionEffect))
|
||
value = new _ActiveSource(
|
||
value.source,
|
||
0
|
||
/* State.Inactive */
|
||
);
|
||
else if (effect.is(setActiveEffect)) {
|
||
for (let active of effect.value)
|
||
if (active.source == value.source)
|
||
value = active;
|
||
}
|
||
}
|
||
return value;
|
||
}
|
||
updateFor(tr, type) {
|
||
return this.map(tr.changes);
|
||
}
|
||
map(changes) {
|
||
return this;
|
||
}
|
||
touches(tr) {
|
||
return tr.changes.touchesRange(cur(tr.state));
|
||
}
|
||
};
|
||
var ActiveResult = class _ActiveResult extends ActiveSource {
|
||
constructor(source, explicit, limit, result, from, to) {
|
||
super(source, 3, explicit);
|
||
this.limit = limit;
|
||
this.result = result;
|
||
this.from = from;
|
||
this.to = to;
|
||
}
|
||
hasResult() {
|
||
return true;
|
||
}
|
||
updateFor(tr, type) {
|
||
var _a;
|
||
if (!(type & 3))
|
||
return this.map(tr.changes);
|
||
let result = this.result;
|
||
if (result.map && !tr.changes.empty)
|
||
result = result.map(result, tr.changes);
|
||
let from = tr.changes.mapPos(this.from), to = tr.changes.mapPos(this.to, 1);
|
||
let pos = cur(tr.state);
|
||
if (pos > to || !result || type & 2 && (cur(tr.startState) == this.from || pos < this.limit))
|
||
return new ActiveSource(
|
||
this.source,
|
||
type & 4 ? 1 : 0
|
||
/* State.Inactive */
|
||
);
|
||
let limit = tr.changes.mapPos(this.limit);
|
||
if (checkValid(result.validFor, tr.state, from, to))
|
||
return new _ActiveResult(this.source, this.explicit, limit, result, from, to);
|
||
if (result.update && (result = result.update(result, from, to, new CompletionContext(tr.state, pos, false))))
|
||
return new _ActiveResult(this.source, this.explicit, limit, result, result.from, (_a = result.to) !== null && _a !== void 0 ? _a : cur(tr.state));
|
||
return new ActiveSource(this.source, 1, this.explicit);
|
||
}
|
||
map(mapping) {
|
||
if (mapping.empty)
|
||
return this;
|
||
let result = this.result.map ? this.result.map(this.result, mapping) : this.result;
|
||
if (!result)
|
||
return new ActiveSource(
|
||
this.source,
|
||
0
|
||
/* State.Inactive */
|
||
);
|
||
return new _ActiveResult(this.source, this.explicit, mapping.mapPos(this.limit), this.result, mapping.mapPos(this.from), mapping.mapPos(this.to, 1));
|
||
}
|
||
touches(tr) {
|
||
return tr.changes.touchesRange(this.from, this.to);
|
||
}
|
||
};
|
||
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;
|
||
}
|
||
var createTooltip = completionTooltip(completionState, applyCompletion);
|
||
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
|
||
/* State.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 MaxUpdateCount = 50;
|
||
var MinAbortTime = 1e3;
|
||
var completionPlugin = ViewPlugin.fromClass(class {
|
||
constructor(view) {
|
||
this.view = view;
|
||
this.debounceUpdate = -1;
|
||
this.running = [];
|
||
this.debounceAccept = -1;
|
||
this.pendingStart = false;
|
||
this.composing = 0;
|
||
for (let active of view.state.field(completionState).active)
|
||
if (active.isPending)
|
||
this.startQuery(active);
|
||
}
|
||
update(update) {
|
||
let cState = update.state.field(completionState);
|
||
let conf = update.state.facet(completionConfig);
|
||
if (!update.selectionSet && !update.docChanged && update.startState.field(completionState) == cState)
|
||
return;
|
||
let doesReset = update.transactions.some((tr) => {
|
||
let type = getUpdateType(tr, conf);
|
||
return type & 8 || (tr.selection || tr.docChanged) && !(type & 3);
|
||
});
|
||
for (let i = 0; i < this.running.length; i++) {
|
||
let query = this.running[i];
|
||
if (doesReset || query.context.abortOnDocChange && update.docChanged || 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);
|
||
if (update.transactions.some((tr) => tr.effects.some((e) => e.is(startCompletionEffect))))
|
||
this.pendingStart = true;
|
||
let delay = this.pendingStart ? 50 : conf.activateOnTypingDelay;
|
||
this.debounceUpdate = cState.active.some((a) => a.isPending && !this.running.some((q) => q.active.source == a.source)) ? setTimeout(() => this.startUpdate(), delay) : -1;
|
||
if (this.composing != 0)
|
||
for (let tr of update.transactions) {
|
||
if (tr.isUserEvent("input.type"))
|
||
this.composing = 2;
|
||
else if (this.composing == 2 && tr.selection)
|
||
this.composing = 3;
|
||
}
|
||
}
|
||
startUpdate() {
|
||
this.debounceUpdate = -1;
|
||
this.pendingStart = false;
|
||
let { state } = this.view, cState = state.field(completionState);
|
||
for (let active of cState.active) {
|
||
if (active.isPending && !this.running.some((r) => r.active.source == active.source))
|
||
this.startQuery(active);
|
||
}
|
||
if (this.running.length && cState.open && cState.open.disabled)
|
||
this.debounceAccept = setTimeout(() => this.accept(), this.view.state.facet(completionConfig).updateSyncTime);
|
||
}
|
||
startQuery(active) {
|
||
let { state } = this.view, pos = cur(state);
|
||
let context = new CompletionContext(state, pos, active.explicit, this.view);
|
||
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(), this.view.state.facet(completionConfig).updateSyncTime);
|
||
}
|
||
// 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), cState = this.view.state.field(completionState);
|
||
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 pos = cur(query.updates.length ? query.updates[0].startState : this.view.state);
|
||
let limit = Math.min(pos, query.done.from + (query.active.explicit ? 0 : 1));
|
||
let active = new ActiveResult(query.active.source, query.active.explicit, limit, query.done, query.done.from, (_a = query.done.to) !== null && _a !== void 0 ? _a : pos);
|
||
for (let tr of query.updates)
|
||
active = active.update(tr, conf);
|
||
if (active.hasResult()) {
|
||
updated.push(active);
|
||
continue;
|
||
}
|
||
}
|
||
let current = cState.active.find((a) => a.source == query.active.source);
|
||
if (current && current.isPending) {
|
||
if (query.done == null) {
|
||
let active = new ActiveSource(
|
||
query.active.source,
|
||
0
|
||
/* State.Inactive */
|
||
);
|
||
for (let tr of query.updates)
|
||
active = active.update(tr, conf);
|
||
if (!active.isPending)
|
||
updated.push(active);
|
||
} else {
|
||
this.startQuery(current);
|
||
}
|
||
}
|
||
}
|
||
if (updated.length || cState.open && cState.open.disabled)
|
||
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))
|
||
setTimeout(() => this.view.dispatch({ effects: closeCompletionEffect.of(null) }), 10);
|
||
}
|
||
},
|
||
compositionstart() {
|
||
this.composing = 1;
|
||
},
|
||
compositionend() {
|
||
if (this.composing == 3) {
|
||
setTimeout(() => this.view.dispatch({ effects: startCompletionEffect.of(false) }), 20);
|
||
}
|
||
this.composing = 0;
|
||
}
|
||
}
|
||
});
|
||
var windows = typeof navigator == "object" && /Win/.test(navigator.platform);
|
||
var commitCharacters = Prec.highest(EditorView.domEventHandlers({
|
||
keydown(event, view) {
|
||
let field = view.state.field(completionState, false);
|
||
if (!field || !field.open || field.open.disabled || field.open.selected < 0 || event.key.length > 1 || event.ctrlKey && !(windows && event.altKey) || event.metaKey)
|
||
return false;
|
||
let option = field.open.options[field.open.selected];
|
||
let result = field.active.find((a) => a.source == option.source);
|
||
let commitChars = option.completion.commitCharacters || result.result.commitCharacters;
|
||
if (commitChars && commitChars.indexOf(event.key) > -1)
|
||
applyCompletion(view, option);
|
||
return false;
|
||
}
|
||
}));
|
||
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",
|
||
whiteSpace: "pre-line"
|
||
},
|
||
".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 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 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)),
|
||
scrollIntoView: true
|
||
}));
|
||
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);
|
||
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)));
|
||
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),
|
||
scrollIntoView: true
|
||
});
|
||
return true;
|
||
}
|
||
});
|
||
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) {
|
||
value = value.map(tr.changes);
|
||
if (tr.selection) {
|
||
let line = tr.state.doc.lineAt(tr.selection.main.head);
|
||
value = value.update({ filter: (from) => from >= line.from && from <= line.to });
|
||
}
|
||
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 [
|
||
commitCharacters,
|
||
completionState,
|
||
completionConfig.of(config2),
|
||
completionPlugin,
|
||
completionKeymapExt,
|
||
baseTheme
|
||
];
|
||
}
|
||
var completionKeymap = [
|
||
{ key: "Ctrl-Space", run: startCompletion },
|
||
{ mac: "Alt-`", 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] : []));
|
||
|
||
// node_modules/crelt/index.js
|
||
function crelt() {
|
||
var elt = arguments[0];
|
||
if (typeof elt == "string") elt = document.createElement(elt);
|
||
var i = 1, next = arguments[1];
|
||
if (next && typeof next == "object" && next.nodeType == null && !Array.isArray(next)) {
|
||
for (var name in next) if (Object.prototype.hasOwnProperty.call(next, name)) {
|
||
var value = next[name];
|
||
if (typeof value == "string") elt.setAttribute(name, value);
|
||
else if (value != null) elt[name] = value;
|
||
}
|
||
i++;
|
||
}
|
||
for (; i < arguments.length; i++) add(elt, arguments[i]);
|
||
return elt;
|
||
}
|
||
function add(elt, child) {
|
||
if (typeof child == "string") {
|
||
elt.appendChild(document.createTextNode(child));
|
||
} else if (child == null) {
|
||
} else if (child.nodeType != null) {
|
||
elt.appendChild(child);
|
||
} else if (Array.isArray(child)) {
|
||
for (var i = 0; i < child.length; i++) add(elt, child[i]);
|
||
} else {
|
||
throw new RangeError("Unsupported child node: " + child);
|
||
}
|
||
}
|
||
|
||
// node_modules/@codemirror/lint/dist/index.js
|
||
var SelectedDiagnostic = class {
|
||
constructor(from, to, diagnostic) {
|
||
this.from = from;
|
||
this.to = to;
|
||
this.diagnostic = diagnostic;
|
||
}
|
||
};
|
||
var LintState = class _LintState {
|
||
constructor(diagnostics, panel, selected) {
|
||
this.diagnostics = diagnostics;
|
||
this.panel = panel;
|
||
this.selected = selected;
|
||
}
|
||
static init(diagnostics, panel, state) {
|
||
let diagnosticFilter = state.facet(lintConfig).markerFilter;
|
||
if (diagnosticFilter)
|
||
diagnostics = diagnosticFilter(diagnostics, state);
|
||
let sorted = diagnostics.slice().sort((a, b) => a.from - b.from || a.to - b.to);
|
||
let deco = new RangeSetBuilder(), active = [], pos = 0;
|
||
for (let i = 0; ; ) {
|
||
let next = i == sorted.length ? null : sorted[i];
|
||
if (!next && !active.length)
|
||
break;
|
||
let from, to;
|
||
if (active.length) {
|
||
from = pos;
|
||
to = active.reduce((p, d) => Math.min(p, d.to), next && next.from > from ? next.from : 1e8);
|
||
} else {
|
||
from = next.from;
|
||
to = next.to;
|
||
active.push(next);
|
||
i++;
|
||
}
|
||
while (i < sorted.length) {
|
||
let next2 = sorted[i];
|
||
if (next2.from == from && (next2.to > next2.from || next2.to == from)) {
|
||
active.push(next2);
|
||
i++;
|
||
to = Math.min(next2.to, to);
|
||
} else {
|
||
to = Math.min(next2.from, to);
|
||
break;
|
||
}
|
||
}
|
||
let sev = maxSeverity(active);
|
||
if (active.some((d) => d.from == d.to || d.from == d.to - 1 && state.doc.lineAt(d.from).to == d.from)) {
|
||
deco.add(from, from, Decoration.widget({
|
||
widget: new DiagnosticWidget(sev),
|
||
diagnostics: active.slice()
|
||
}));
|
||
} else {
|
||
let markClass = active.reduce((c, d) => d.markClass ? c + " " + d.markClass : c, "");
|
||
deco.add(from, to, Decoration.mark({
|
||
class: "cm-lintRange cm-lintRange-" + sev + markClass,
|
||
diagnostics: active.slice(),
|
||
inclusiveEnd: active.some((a) => a.to > to)
|
||
}));
|
||
}
|
||
pos = to;
|
||
for (let i2 = 0; i2 < active.length; i2++)
|
||
if (active[i2].to <= pos)
|
||
active.splice(i2--, 1);
|
||
}
|
||
let set = deco.finish();
|
||
return new _LintState(set, panel, findDiagnostic(set));
|
||
}
|
||
};
|
||
function findDiagnostic(diagnostics, diagnostic = null, after = 0) {
|
||
let found = null;
|
||
diagnostics.between(after, 1e9, (from, to, { spec }) => {
|
||
if (diagnostic && spec.diagnostics.indexOf(diagnostic) < 0)
|
||
return;
|
||
if (!found)
|
||
found = new SelectedDiagnostic(from, to, diagnostic || spec.diagnostics[0]);
|
||
else if (spec.diagnostics.indexOf(found.diagnostic) < 0)
|
||
return false;
|
||
else
|
||
found = new SelectedDiagnostic(found.from, to, found.diagnostic);
|
||
});
|
||
return found;
|
||
}
|
||
function hideTooltip(tr, tooltip) {
|
||
let from = tooltip.pos, to = tooltip.end || from;
|
||
let result = tr.state.facet(lintConfig).hideOn(tr, from, to);
|
||
if (result != null)
|
||
return result;
|
||
let line = tr.startState.doc.lineAt(tooltip.pos);
|
||
return !!(tr.effects.some((e) => e.is(setDiagnosticsEffect)) || tr.changes.touchesRange(line.from, Math.max(line.to, to)));
|
||
}
|
||
function maybeEnableLint(state, effects) {
|
||
return state.field(lintState, false) ? effects : effects.concat(StateEffect.appendConfig.of(lintExtensions));
|
||
}
|
||
function setDiagnostics(state, diagnostics) {
|
||
return {
|
||
effects: maybeEnableLint(state, [setDiagnosticsEffect.of(diagnostics)])
|
||
};
|
||
}
|
||
var setDiagnosticsEffect = StateEffect.define();
|
||
var togglePanel = StateEffect.define();
|
||
var movePanelSelection = StateEffect.define();
|
||
var lintState = StateField.define({
|
||
create() {
|
||
return new LintState(Decoration.none, null, null);
|
||
},
|
||
update(value, tr) {
|
||
if (tr.docChanged && value.diagnostics.size) {
|
||
let mapped = value.diagnostics.map(tr.changes), selected = null, panel = value.panel;
|
||
if (value.selected) {
|
||
let selPos = tr.changes.mapPos(value.selected.from, 1);
|
||
selected = findDiagnostic(mapped, value.selected.diagnostic, selPos) || findDiagnostic(mapped, null, selPos);
|
||
}
|
||
if (!mapped.size && panel && tr.state.facet(lintConfig).autoPanel)
|
||
panel = null;
|
||
value = new LintState(mapped, panel, selected);
|
||
}
|
||
for (let effect of tr.effects) {
|
||
if (effect.is(setDiagnosticsEffect)) {
|
||
let panel = !tr.state.facet(lintConfig).autoPanel ? value.panel : effect.value.length ? LintPanel.open : null;
|
||
value = LintState.init(effect.value, panel, tr.state);
|
||
} else if (effect.is(togglePanel)) {
|
||
value = new LintState(value.diagnostics, effect.value ? LintPanel.open : null, value.selected);
|
||
} else if (effect.is(movePanelSelection)) {
|
||
value = new LintState(value.diagnostics, value.panel, effect.value);
|
||
}
|
||
}
|
||
return value;
|
||
},
|
||
provide: (f) => [
|
||
showPanel.from(f, (val) => val.panel),
|
||
EditorView.decorations.from(f, (s) => s.diagnostics)
|
||
]
|
||
});
|
||
var activeMark = Decoration.mark({ class: "cm-lintRange cm-lintRange-active" });
|
||
function lintTooltip(view, pos, side) {
|
||
let { diagnostics } = view.state.field(lintState);
|
||
let found, start = -1, end = -1;
|
||
diagnostics.between(pos - (side < 0 ? 1 : 0), pos + (side > 0 ? 1 : 0), (from, to, { spec }) => {
|
||
if (pos >= from && pos <= to && (from == to || (pos > from || side > 0) && (pos < to || side < 0))) {
|
||
found = spec.diagnostics;
|
||
start = from;
|
||
end = to;
|
||
return false;
|
||
}
|
||
});
|
||
let diagnosticFilter = view.state.facet(lintConfig).tooltipFilter;
|
||
if (found && diagnosticFilter)
|
||
found = diagnosticFilter(found, view.state);
|
||
if (!found)
|
||
return null;
|
||
return {
|
||
pos: start,
|
||
end,
|
||
above: view.state.doc.lineAt(start).to < end,
|
||
create() {
|
||
return { dom: diagnosticsTooltip(view, found) };
|
||
}
|
||
};
|
||
}
|
||
function diagnosticsTooltip(view, diagnostics) {
|
||
return crelt("ul", { class: "cm-tooltip-lint" }, diagnostics.map((d) => renderDiagnostic(view, d, false)));
|
||
}
|
||
var openLintPanel = (view) => {
|
||
let field = view.state.field(lintState, false);
|
||
if (!field || !field.panel)
|
||
view.dispatch({ effects: maybeEnableLint(view.state, [togglePanel.of(true)]) });
|
||
let panel = getPanel(view, LintPanel.open);
|
||
if (panel)
|
||
panel.dom.querySelector(".cm-panel-lint ul").focus();
|
||
return true;
|
||
};
|
||
var closeLintPanel = (view) => {
|
||
let field = view.state.field(lintState, false);
|
||
if (!field || !field.panel)
|
||
return false;
|
||
view.dispatch({ effects: togglePanel.of(false) });
|
||
return true;
|
||
};
|
||
var nextDiagnostic = (view) => {
|
||
let field = view.state.field(lintState, false);
|
||
if (!field)
|
||
return false;
|
||
let sel = view.state.selection.main, next = field.diagnostics.iter(sel.to + 1);
|
||
if (!next.value) {
|
||
next = field.diagnostics.iter(0);
|
||
if (!next.value || next.from == sel.from && next.to == sel.to)
|
||
return false;
|
||
}
|
||
view.dispatch({ selection: { anchor: next.from, head: next.to }, scrollIntoView: true });
|
||
return true;
|
||
};
|
||
var lintKeymap = [
|
||
{ key: "Mod-Shift-m", run: openLintPanel, preventDefault: true },
|
||
{ key: "F8", run: nextDiagnostic }
|
||
];
|
||
var lintPlugin = ViewPlugin.fromClass(class {
|
||
constructor(view) {
|
||
this.view = view;
|
||
this.timeout = -1;
|
||
this.set = true;
|
||
let { delay } = view.state.facet(lintConfig);
|
||
this.lintTime = Date.now() + delay;
|
||
this.run = this.run.bind(this);
|
||
this.timeout = setTimeout(this.run, delay);
|
||
}
|
||
run() {
|
||
clearTimeout(this.timeout);
|
||
let now = Date.now();
|
||
if (now < this.lintTime - 10) {
|
||
this.timeout = setTimeout(this.run, this.lintTime - now);
|
||
} else {
|
||
this.set = false;
|
||
let { state } = this.view, { sources } = state.facet(lintConfig);
|
||
if (sources.length)
|
||
batchResults(sources.map((s) => Promise.resolve(s(this.view))), (annotations) => {
|
||
if (this.view.state.doc == state.doc)
|
||
this.view.dispatch(setDiagnostics(this.view.state, annotations.reduce((a, b) => a.concat(b))));
|
||
}, (error) => {
|
||
logException(this.view.state, error);
|
||
});
|
||
}
|
||
}
|
||
update(update) {
|
||
let config2 = update.state.facet(lintConfig);
|
||
if (update.docChanged || config2 != update.startState.facet(lintConfig) || config2.needsRefresh && config2.needsRefresh(update)) {
|
||
this.lintTime = Date.now() + config2.delay;
|
||
if (!this.set) {
|
||
this.set = true;
|
||
this.timeout = setTimeout(this.run, config2.delay);
|
||
}
|
||
}
|
||
}
|
||
force() {
|
||
if (this.set) {
|
||
this.lintTime = Date.now();
|
||
this.run();
|
||
}
|
||
}
|
||
destroy() {
|
||
clearTimeout(this.timeout);
|
||
}
|
||
});
|
||
function batchResults(promises, sink, error) {
|
||
let collected = [], timeout = -1;
|
||
for (let p of promises)
|
||
p.then((value) => {
|
||
collected.push(value);
|
||
clearTimeout(timeout);
|
||
if (collected.length == promises.length)
|
||
sink(collected);
|
||
else
|
||
timeout = setTimeout(() => sink(collected), 200);
|
||
}, error);
|
||
}
|
||
var lintConfig = Facet.define({
|
||
combine(input) {
|
||
return Object.assign({ sources: input.map((i) => i.source).filter((x) => x != null) }, combineConfig(input.map((i) => i.config), {
|
||
delay: 750,
|
||
markerFilter: null,
|
||
tooltipFilter: null,
|
||
needsRefresh: null,
|
||
hideOn: () => null
|
||
}, {
|
||
needsRefresh: (a, b) => !a ? b : !b ? a : (u) => a(u) || b(u)
|
||
}));
|
||
}
|
||
});
|
||
function assignKeys(actions) {
|
||
let assigned = [];
|
||
if (actions)
|
||
actions: for (let { name } of actions) {
|
||
for (let i = 0; i < name.length; i++) {
|
||
let ch = name[i];
|
||
if (/[a-zA-Z]/.test(ch) && !assigned.some((c) => c.toLowerCase() == ch.toLowerCase())) {
|
||
assigned.push(ch);
|
||
continue actions;
|
||
}
|
||
}
|
||
assigned.push("");
|
||
}
|
||
return assigned;
|
||
}
|
||
function renderDiagnostic(view, diagnostic, inPanel) {
|
||
var _a;
|
||
let keys = inPanel ? assignKeys(diagnostic.actions) : [];
|
||
return crelt("li", { class: "cm-diagnostic cm-diagnostic-" + diagnostic.severity }, crelt("span", { class: "cm-diagnosticText" }, diagnostic.renderMessage ? diagnostic.renderMessage(view) : diagnostic.message), (_a = diagnostic.actions) === null || _a === void 0 ? void 0 : _a.map((action, i) => {
|
||
let fired = false, click = (e) => {
|
||
e.preventDefault();
|
||
if (fired)
|
||
return;
|
||
fired = true;
|
||
let found = findDiagnostic(view.state.field(lintState).diagnostics, diagnostic);
|
||
if (found)
|
||
action.apply(view, found.from, found.to);
|
||
};
|
||
let { name } = action, keyIndex = keys[i] ? name.indexOf(keys[i]) : -1;
|
||
let nameElt = keyIndex < 0 ? name : [
|
||
name.slice(0, keyIndex),
|
||
crelt("u", name.slice(keyIndex, keyIndex + 1)),
|
||
name.slice(keyIndex + 1)
|
||
];
|
||
return crelt("button", {
|
||
type: "button",
|
||
class: "cm-diagnosticAction",
|
||
onclick: click,
|
||
onmousedown: click,
|
||
"aria-label": ` Action: ${name}${keyIndex < 0 ? "" : ` (access key "${keys[i]})"`}.`
|
||
}, nameElt);
|
||
}), diagnostic.source && crelt("div", { class: "cm-diagnosticSource" }, diagnostic.source));
|
||
}
|
||
var DiagnosticWidget = class extends WidgetType {
|
||
constructor(sev) {
|
||
super();
|
||
this.sev = sev;
|
||
}
|
||
eq(other) {
|
||
return other.sev == this.sev;
|
||
}
|
||
toDOM() {
|
||
return crelt("span", { class: "cm-lintPoint cm-lintPoint-" + this.sev });
|
||
}
|
||
};
|
||
var PanelItem = class {
|
||
constructor(view, diagnostic) {
|
||
this.diagnostic = diagnostic;
|
||
this.id = "item_" + Math.floor(Math.random() * 4294967295).toString(16);
|
||
this.dom = renderDiagnostic(view, diagnostic, true);
|
||
this.dom.id = this.id;
|
||
this.dom.setAttribute("role", "option");
|
||
}
|
||
};
|
||
var LintPanel = class _LintPanel {
|
||
constructor(view) {
|
||
this.view = view;
|
||
this.items = [];
|
||
let onkeydown = (event) => {
|
||
if (event.keyCode == 27) {
|
||
closeLintPanel(this.view);
|
||
this.view.focus();
|
||
} else if (event.keyCode == 38 || event.keyCode == 33) {
|
||
this.moveSelection((this.selectedIndex - 1 + this.items.length) % this.items.length);
|
||
} else if (event.keyCode == 40 || event.keyCode == 34) {
|
||
this.moveSelection((this.selectedIndex + 1) % this.items.length);
|
||
} else if (event.keyCode == 36) {
|
||
this.moveSelection(0);
|
||
} else if (event.keyCode == 35) {
|
||
this.moveSelection(this.items.length - 1);
|
||
} else if (event.keyCode == 13) {
|
||
this.view.focus();
|
||
} else if (event.keyCode >= 65 && event.keyCode <= 90 && this.selectedIndex >= 0) {
|
||
let { diagnostic } = this.items[this.selectedIndex], keys = assignKeys(diagnostic.actions);
|
||
for (let i = 0; i < keys.length; i++)
|
||
if (keys[i].toUpperCase().charCodeAt(0) == event.keyCode) {
|
||
let found = findDiagnostic(this.view.state.field(lintState).diagnostics, diagnostic);
|
||
if (found)
|
||
diagnostic.actions[i].apply(view, found.from, found.to);
|
||
}
|
||
} else {
|
||
return;
|
||
}
|
||
event.preventDefault();
|
||
};
|
||
let onclick = (event) => {
|
||
for (let i = 0; i < this.items.length; i++) {
|
||
if (this.items[i].dom.contains(event.target))
|
||
this.moveSelection(i);
|
||
}
|
||
};
|
||
this.list = crelt("ul", {
|
||
tabIndex: 0,
|
||
role: "listbox",
|
||
"aria-label": this.view.state.phrase("Diagnostics"),
|
||
onkeydown,
|
||
onclick
|
||
});
|
||
this.dom = crelt("div", { class: "cm-panel-lint" }, this.list, crelt("button", {
|
||
type: "button",
|
||
name: "close",
|
||
"aria-label": this.view.state.phrase("close"),
|
||
onclick: () => closeLintPanel(this.view)
|
||
}, "×"));
|
||
this.update();
|
||
}
|
||
get selectedIndex() {
|
||
let selected = this.view.state.field(lintState).selected;
|
||
if (!selected)
|
||
return -1;
|
||
for (let i = 0; i < this.items.length; i++)
|
||
if (this.items[i].diagnostic == selected.diagnostic)
|
||
return i;
|
||
return -1;
|
||
}
|
||
update() {
|
||
let { diagnostics, selected } = this.view.state.field(lintState);
|
||
let i = 0, needsSync = false, newSelectedItem = null;
|
||
let seen = /* @__PURE__ */ new Set();
|
||
diagnostics.between(0, this.view.state.doc.length, (_start, _end, { spec }) => {
|
||
for (let diagnostic of spec.diagnostics) {
|
||
if (seen.has(diagnostic))
|
||
continue;
|
||
seen.add(diagnostic);
|
||
let found = -1, item;
|
||
for (let j = i; j < this.items.length; j++)
|
||
if (this.items[j].diagnostic == diagnostic) {
|
||
found = j;
|
||
break;
|
||
}
|
||
if (found < 0) {
|
||
item = new PanelItem(this.view, diagnostic);
|
||
this.items.splice(i, 0, item);
|
||
needsSync = true;
|
||
} else {
|
||
item = this.items[found];
|
||
if (found > i) {
|
||
this.items.splice(i, found - i);
|
||
needsSync = true;
|
||
}
|
||
}
|
||
if (selected && item.diagnostic == selected.diagnostic) {
|
||
if (!item.dom.hasAttribute("aria-selected")) {
|
||
item.dom.setAttribute("aria-selected", "true");
|
||
newSelectedItem = item;
|
||
}
|
||
} else if (item.dom.hasAttribute("aria-selected")) {
|
||
item.dom.removeAttribute("aria-selected");
|
||
}
|
||
i++;
|
||
}
|
||
});
|
||
while (i < this.items.length && !(this.items.length == 1 && this.items[0].diagnostic.from < 0)) {
|
||
needsSync = true;
|
||
this.items.pop();
|
||
}
|
||
if (this.items.length == 0) {
|
||
this.items.push(new PanelItem(this.view, {
|
||
from: -1,
|
||
to: -1,
|
||
severity: "info",
|
||
message: this.view.state.phrase("No diagnostics")
|
||
}));
|
||
needsSync = true;
|
||
}
|
||
if (newSelectedItem) {
|
||
this.list.setAttribute("aria-activedescendant", newSelectedItem.id);
|
||
this.view.requestMeasure({
|
||
key: this,
|
||
read: () => ({ sel: newSelectedItem.dom.getBoundingClientRect(), panel: this.list.getBoundingClientRect() }),
|
||
write: ({ sel, panel }) => {
|
||
let scaleY = panel.height / this.list.offsetHeight;
|
||
if (sel.top < panel.top)
|
||
this.list.scrollTop -= (panel.top - sel.top) / scaleY;
|
||
else if (sel.bottom > panel.bottom)
|
||
this.list.scrollTop += (sel.bottom - panel.bottom) / scaleY;
|
||
}
|
||
});
|
||
} else if (this.selectedIndex < 0) {
|
||
this.list.removeAttribute("aria-activedescendant");
|
||
}
|
||
if (needsSync)
|
||
this.sync();
|
||
}
|
||
sync() {
|
||
let domPos = this.list.firstChild;
|
||
function rm() {
|
||
let prev = domPos;
|
||
domPos = prev.nextSibling;
|
||
prev.remove();
|
||
}
|
||
for (let item of this.items) {
|
||
if (item.dom.parentNode == this.list) {
|
||
while (domPos != item.dom)
|
||
rm();
|
||
domPos = item.dom.nextSibling;
|
||
} else {
|
||
this.list.insertBefore(item.dom, domPos);
|
||
}
|
||
}
|
||
while (domPos)
|
||
rm();
|
||
}
|
||
moveSelection(selectedIndex) {
|
||
if (this.selectedIndex < 0)
|
||
return;
|
||
let field = this.view.state.field(lintState);
|
||
let selection = findDiagnostic(field.diagnostics, this.items[selectedIndex].diagnostic);
|
||
if (!selection)
|
||
return;
|
||
this.view.dispatch({
|
||
selection: { anchor: selection.from, head: selection.to },
|
||
scrollIntoView: true,
|
||
effects: movePanelSelection.of(selection)
|
||
});
|
||
}
|
||
static open(view) {
|
||
return new _LintPanel(view);
|
||
}
|
||
};
|
||
function svg(content, attrs = `viewBox="0 0 40 40"`) {
|
||
return `url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" ${attrs}>${encodeURIComponent(content)}</svg>')`;
|
||
}
|
||
function underline(color) {
|
||
return svg(`<path d="m0 2.5 l2 -1.5 l1 0 l2 1.5 l1 0" stroke="${color}" fill="none" stroke-width=".7"/>`, `width="6" height="3"`);
|
||
}
|
||
var baseTheme2 = EditorView.baseTheme({
|
||
".cm-diagnostic": {
|
||
padding: "3px 6px 3px 8px",
|
||
marginLeft: "-1px",
|
||
display: "block",
|
||
whiteSpace: "pre-wrap"
|
||
},
|
||
".cm-diagnostic-error": { borderLeft: "5px solid #d11" },
|
||
".cm-diagnostic-warning": { borderLeft: "5px solid orange" },
|
||
".cm-diagnostic-info": { borderLeft: "5px solid #999" },
|
||
".cm-diagnostic-hint": { borderLeft: "5px solid #66d" },
|
||
".cm-diagnosticAction": {
|
||
font: "inherit",
|
||
border: "none",
|
||
padding: "2px 4px",
|
||
backgroundColor: "#444",
|
||
color: "white",
|
||
borderRadius: "3px",
|
||
marginLeft: "8px",
|
||
cursor: "pointer"
|
||
},
|
||
".cm-diagnosticSource": {
|
||
fontSize: "70%",
|
||
opacity: 0.7
|
||
},
|
||
".cm-lintRange": {
|
||
backgroundPosition: "left bottom",
|
||
backgroundRepeat: "repeat-x",
|
||
paddingBottom: "0.7px"
|
||
},
|
||
".cm-lintRange-error": { backgroundImage: underline("#d11") },
|
||
".cm-lintRange-warning": { backgroundImage: underline("orange") },
|
||
".cm-lintRange-info": { backgroundImage: underline("#999") },
|
||
".cm-lintRange-hint": { backgroundImage: underline("#66d") },
|
||
".cm-lintRange-active": { backgroundColor: "#ffdd9980" },
|
||
".cm-tooltip-lint": {
|
||
padding: 0,
|
||
margin: 0
|
||
},
|
||
".cm-lintPoint": {
|
||
position: "relative",
|
||
"&:after": {
|
||
content: '""',
|
||
position: "absolute",
|
||
bottom: 0,
|
||
left: "-2px",
|
||
borderLeft: "3px solid transparent",
|
||
borderRight: "3px solid transparent",
|
||
borderBottom: "4px solid #d11"
|
||
}
|
||
},
|
||
".cm-lintPoint-warning": {
|
||
"&:after": { borderBottomColor: "orange" }
|
||
},
|
||
".cm-lintPoint-info": {
|
||
"&:after": { borderBottomColor: "#999" }
|
||
},
|
||
".cm-lintPoint-hint": {
|
||
"&:after": { borderBottomColor: "#66d" }
|
||
},
|
||
".cm-panel.cm-panel-lint": {
|
||
position: "relative",
|
||
"& ul": {
|
||
maxHeight: "100px",
|
||
overflowY: "auto",
|
||
"& [aria-selected]": {
|
||
backgroundColor: "#ddd",
|
||
"& u": { textDecoration: "underline" }
|
||
},
|
||
"&:focus [aria-selected]": {
|
||
background_fallback: "#bdf",
|
||
backgroundColor: "Highlight",
|
||
color_fallback: "white",
|
||
color: "HighlightText"
|
||
},
|
||
"& u": { textDecoration: "none" },
|
||
padding: 0,
|
||
margin: 0
|
||
},
|
||
"& [name=close]": {
|
||
position: "absolute",
|
||
top: "0",
|
||
right: "2px",
|
||
background: "inherit",
|
||
border: "none",
|
||
font: "inherit",
|
||
padding: 0,
|
||
margin: 0
|
||
}
|
||
}
|
||
});
|
||
function severityWeight(sev) {
|
||
return sev == "error" ? 4 : sev == "warning" ? 3 : sev == "info" ? 2 : 1;
|
||
}
|
||
function maxSeverity(diagnostics) {
|
||
let sev = "hint", weight = 1;
|
||
for (let d of diagnostics) {
|
||
let w = severityWeight(d.severity);
|
||
if (w > weight) {
|
||
weight = w;
|
||
sev = d.severity;
|
||
}
|
||
}
|
||
return sev;
|
||
}
|
||
var LintGutterMarker = class extends GutterMarker {
|
||
constructor(diagnostics) {
|
||
super();
|
||
this.diagnostics = diagnostics;
|
||
this.severity = maxSeverity(diagnostics);
|
||
}
|
||
toDOM(view) {
|
||
let elt = document.createElement("div");
|
||
elt.className = "cm-lint-marker cm-lint-marker-" + this.severity;
|
||
let diagnostics = this.diagnostics;
|
||
let diagnosticsFilter = view.state.facet(lintGutterConfig).tooltipFilter;
|
||
if (diagnosticsFilter)
|
||
diagnostics = diagnosticsFilter(diagnostics, view.state);
|
||
if (diagnostics.length)
|
||
elt.onmouseover = () => gutterMarkerMouseOver(view, elt, diagnostics);
|
||
return elt;
|
||
}
|
||
};
|
||
function trackHoverOn(view, marker) {
|
||
let mousemove = (event) => {
|
||
let rect = marker.getBoundingClientRect();
|
||
if (event.clientX > rect.left - 10 && event.clientX < rect.right + 10 && event.clientY > rect.top - 10 && event.clientY < rect.bottom + 10)
|
||
return;
|
||
for (let target = event.target; target; target = target.parentNode) {
|
||
if (target.nodeType == 1 && target.classList.contains("cm-tooltip-lint"))
|
||
return;
|
||
}
|
||
window.removeEventListener("mousemove", mousemove);
|
||
if (view.state.field(lintGutterTooltip))
|
||
view.dispatch({ effects: setLintGutterTooltip.of(null) });
|
||
};
|
||
window.addEventListener("mousemove", mousemove);
|
||
}
|
||
function gutterMarkerMouseOver(view, marker, diagnostics) {
|
||
function hovered() {
|
||
let line = view.elementAtHeight(marker.getBoundingClientRect().top + 5 - view.documentTop);
|
||
const linePos = view.coordsAtPos(line.from);
|
||
if (linePos) {
|
||
view.dispatch({ effects: setLintGutterTooltip.of({
|
||
pos: line.from,
|
||
above: false,
|
||
clip: false,
|
||
create() {
|
||
return {
|
||
dom: diagnosticsTooltip(view, diagnostics),
|
||
getCoords: () => marker.getBoundingClientRect()
|
||
};
|
||
}
|
||
}) });
|
||
}
|
||
marker.onmouseout = marker.onmousemove = null;
|
||
trackHoverOn(view, marker);
|
||
}
|
||
let { hoverTime } = view.state.facet(lintGutterConfig);
|
||
let hoverTimeout = setTimeout(hovered, hoverTime);
|
||
marker.onmouseout = () => {
|
||
clearTimeout(hoverTimeout);
|
||
marker.onmouseout = marker.onmousemove = null;
|
||
};
|
||
marker.onmousemove = () => {
|
||
clearTimeout(hoverTimeout);
|
||
hoverTimeout = setTimeout(hovered, hoverTime);
|
||
};
|
||
}
|
||
function markersForDiagnostics(doc, diagnostics) {
|
||
let byLine = /* @__PURE__ */ Object.create(null);
|
||
for (let diagnostic of diagnostics) {
|
||
let line = doc.lineAt(diagnostic.from);
|
||
(byLine[line.from] || (byLine[line.from] = [])).push(diagnostic);
|
||
}
|
||
let markers = [];
|
||
for (let line in byLine) {
|
||
markers.push(new LintGutterMarker(byLine[line]).range(+line));
|
||
}
|
||
return RangeSet.of(markers, true);
|
||
}
|
||
var lintGutterExtension = gutter({
|
||
class: "cm-gutter-lint",
|
||
markers: (view) => view.state.field(lintGutterMarkers),
|
||
widgetMarker: (view, widget, block) => {
|
||
let diagnostics = [];
|
||
view.state.field(lintGutterMarkers).between(block.from, block.to, (from, to, value) => {
|
||
if (from > block.from && from < block.to)
|
||
diagnostics.push(...value.diagnostics);
|
||
});
|
||
return diagnostics.length ? new LintGutterMarker(diagnostics) : null;
|
||
}
|
||
});
|
||
var lintGutterMarkers = StateField.define({
|
||
create() {
|
||
return RangeSet.empty;
|
||
},
|
||
update(markers, tr) {
|
||
markers = markers.map(tr.changes);
|
||
let diagnosticFilter = tr.state.facet(lintGutterConfig).markerFilter;
|
||
for (let effect of tr.effects) {
|
||
if (effect.is(setDiagnosticsEffect)) {
|
||
let diagnostics = effect.value;
|
||
if (diagnosticFilter)
|
||
diagnostics = diagnosticFilter(diagnostics || [], tr.state);
|
||
markers = markersForDiagnostics(tr.state.doc, diagnostics.slice(0));
|
||
}
|
||
}
|
||
return markers;
|
||
}
|
||
});
|
||
var setLintGutterTooltip = StateEffect.define();
|
||
var lintGutterTooltip = StateField.define({
|
||
create() {
|
||
return null;
|
||
},
|
||
update(tooltip, tr) {
|
||
if (tooltip && tr.docChanged)
|
||
tooltip = hideTooltip(tr, tooltip) ? null : Object.assign(Object.assign({}, tooltip), { pos: tr.changes.mapPos(tooltip.pos) });
|
||
return tr.effects.reduce((t, e) => e.is(setLintGutterTooltip) ? e.value : t, tooltip);
|
||
},
|
||
provide: (field) => showTooltip.from(field)
|
||
});
|
||
var lintGutterTheme = EditorView.baseTheme({
|
||
".cm-gutter-lint": {
|
||
width: "1.4em",
|
||
"& .cm-gutterElement": {
|
||
padding: ".2em"
|
||
}
|
||
},
|
||
".cm-lint-marker": {
|
||
width: "1em",
|
||
height: "1em"
|
||
},
|
||
".cm-lint-marker-info": {
|
||
content: svg(`<path fill="#aaf" stroke="#77e" stroke-width="6" stroke-linejoin="round" d="M5 5L35 5L35 35L5 35Z"/>`)
|
||
},
|
||
".cm-lint-marker-warning": {
|
||
content: svg(`<path fill="#fe8" stroke="#fd7" stroke-width="6" stroke-linejoin="round" d="M20 6L37 35L3 35Z"/>`)
|
||
},
|
||
".cm-lint-marker-error": {
|
||
content: svg(`<circle cx="20" cy="20" r="15" fill="#f87" stroke="#f43" stroke-width="6"/>`)
|
||
}
|
||
});
|
||
var lintExtensions = [
|
||
lintState,
|
||
EditorView.decorations.compute([lintState], (state) => {
|
||
let { selected, panel } = state.field(lintState);
|
||
return !selected || !panel || selected.from == selected.to ? Decoration.none : Decoration.set([
|
||
activeMark.range(selected.from, selected.to)
|
||
]);
|
||
}),
|
||
hoverTooltip(lintTooltip, { hideOn: hideTooltip }),
|
||
baseTheme2
|
||
];
|
||
var lintGutterConfig = Facet.define({
|
||
combine(configs) {
|
||
return combineConfig(configs, {
|
||
hoverTime: 300,
|
||
markerFilter: null,
|
||
tooltipFilter: null
|
||
});
|
||
}
|
||
});
|
||
|
||
export {
|
||
insertCompletionText,
|
||
closeBrackets,
|
||
closeBracketsKeymap,
|
||
autocompletion,
|
||
completionKeymap,
|
||
crelt,
|
||
setDiagnostics,
|
||
lintKeymap
|
||
};
|
||
//# sourceMappingURL=chunk-HCLPPKHD.js.map
|