2212 lines
69 KiB
JavaScript
2212 lines
69 KiB
JavaScript
import {
|
||
Decoration,
|
||
EditorView,
|
||
GutterMarker,
|
||
StyleModule,
|
||
ViewPlugin,
|
||
WidgetType,
|
||
gutter,
|
||
logException
|
||
} from "./chunk-LORPBXGU.js";
|
||
import {
|
||
EditorState,
|
||
Facet,
|
||
Prec,
|
||
RangeSet,
|
||
RangeSetBuilder,
|
||
StateEffect,
|
||
StateField,
|
||
combineConfig,
|
||
countColumn
|
||
} from "./chunk-MKFMOIK6.js";
|
||
import {
|
||
IterMode,
|
||
NodeProp,
|
||
NodeSet,
|
||
NodeType,
|
||
Parser,
|
||
Tree,
|
||
TreeFragment,
|
||
highlightTree,
|
||
styleTags,
|
||
tagHighlighter,
|
||
tags
|
||
} from "./chunk-BSVZPYOD.js";
|
||
|
||
// node_modules/@codemirror/language/dist/index.js
|
||
var _a;
|
||
var languageDataProp = new NodeProp();
|
||
function defineLanguageFacet(baseData) {
|
||
return Facet.define({
|
||
combine: baseData ? (values) => values.concat(baseData) : void 0
|
||
});
|
||
}
|
||
var sublanguageProp = new NodeProp();
|
||
var Language = class {
|
||
/**
|
||
Construct a language object. If you need to invoke this
|
||
directly, first define a data facet with
|
||
[`defineLanguageFacet`](https://codemirror.net/6/docs/ref/#language.defineLanguageFacet), and then
|
||
configure your parser to [attach](https://codemirror.net/6/docs/ref/#language.languageDataProp) it
|
||
to the language's outer syntax node.
|
||
*/
|
||
constructor(data, parser, extraExtensions = [], name = "") {
|
||
this.data = data;
|
||
this.name = name;
|
||
if (!EditorState.prototype.hasOwnProperty("tree"))
|
||
Object.defineProperty(EditorState.prototype, "tree", { get() {
|
||
return syntaxTree(this);
|
||
} });
|
||
this.parser = parser;
|
||
this.extension = [
|
||
language.of(this),
|
||
EditorState.languageData.of((state, pos, side) => {
|
||
let top = topNodeAt(state, pos, side), data2 = top.type.prop(languageDataProp);
|
||
if (!data2)
|
||
return [];
|
||
let base = state.facet(data2), sub = top.type.prop(sublanguageProp);
|
||
if (sub) {
|
||
let innerNode = top.resolve(pos - top.from, side);
|
||
for (let sublang of sub)
|
||
if (sublang.test(innerNode, state)) {
|
||
let data3 = state.facet(sublang.facet);
|
||
return sublang.type == "replace" ? data3 : data3.concat(base);
|
||
}
|
||
}
|
||
return base;
|
||
})
|
||
].concat(extraExtensions);
|
||
}
|
||
/**
|
||
Query whether this language is active at the given position.
|
||
*/
|
||
isActiveAt(state, pos, side = -1) {
|
||
return topNodeAt(state, pos, side).type.prop(languageDataProp) == this.data;
|
||
}
|
||
/**
|
||
Find the document regions that were parsed using this language.
|
||
The returned regions will _include_ any nested languages rooted
|
||
in this language, when those exist.
|
||
*/
|
||
findRegions(state) {
|
||
let lang = state.facet(language);
|
||
if ((lang === null || lang === void 0 ? void 0 : lang.data) == this.data)
|
||
return [{ from: 0, to: state.doc.length }];
|
||
if (!lang || !lang.allowsNesting)
|
||
return [];
|
||
let result = [];
|
||
let explore = (tree, from) => {
|
||
if (tree.prop(languageDataProp) == this.data) {
|
||
result.push({ from, to: from + tree.length });
|
||
return;
|
||
}
|
||
let mount = tree.prop(NodeProp.mounted);
|
||
if (mount) {
|
||
if (mount.tree.prop(languageDataProp) == this.data) {
|
||
if (mount.overlay)
|
||
for (let r of mount.overlay)
|
||
result.push({ from: r.from + from, to: r.to + from });
|
||
else
|
||
result.push({ from, to: from + tree.length });
|
||
return;
|
||
} else if (mount.overlay) {
|
||
let size = result.length;
|
||
explore(mount.tree, mount.overlay[0].from + from);
|
||
if (result.length > size)
|
||
return;
|
||
}
|
||
}
|
||
for (let i = 0; i < tree.children.length; i++) {
|
||
let ch = tree.children[i];
|
||
if (ch instanceof Tree)
|
||
explore(ch, tree.positions[i] + from);
|
||
}
|
||
};
|
||
explore(syntaxTree(state), 0);
|
||
return result;
|
||
}
|
||
/**
|
||
Indicates whether this language allows nested languages. The
|
||
default implementation returns true.
|
||
*/
|
||
get allowsNesting() {
|
||
return true;
|
||
}
|
||
};
|
||
Language.setState = StateEffect.define();
|
||
function topNodeAt(state, pos, side) {
|
||
let topLang = state.facet(language), tree = syntaxTree(state).topNode;
|
||
if (!topLang || topLang.allowsNesting) {
|
||
for (let node = tree; node; node = node.enter(pos, side, IterMode.ExcludeBuffers))
|
||
if (node.type.isTop)
|
||
tree = node;
|
||
}
|
||
return tree;
|
||
}
|
||
var LRLanguage = class _LRLanguage extends Language {
|
||
constructor(data, parser, name) {
|
||
super(data, parser, [], name);
|
||
this.parser = parser;
|
||
}
|
||
/**
|
||
Define a language from a parser.
|
||
*/
|
||
static define(spec) {
|
||
let data = defineLanguageFacet(spec.languageData);
|
||
return new _LRLanguage(data, spec.parser.configure({
|
||
props: [languageDataProp.add((type) => type.isTop ? data : void 0)]
|
||
}), spec.name);
|
||
}
|
||
/**
|
||
Create a new instance of this language with a reconfigured
|
||
version of its parser and optionally a new name.
|
||
*/
|
||
configure(options, name) {
|
||
return new _LRLanguage(this.data, this.parser.configure(options), name || this.name);
|
||
}
|
||
get allowsNesting() {
|
||
return this.parser.hasWrappers();
|
||
}
|
||
};
|
||
function syntaxTree(state) {
|
||
let field = state.field(Language.state, false);
|
||
return field ? field.tree : Tree.empty;
|
||
}
|
||
function ensureSyntaxTree(state, upto, timeout = 50) {
|
||
var _a2;
|
||
let parse = (_a2 = state.field(Language.state, false)) === null || _a2 === void 0 ? void 0 : _a2.context;
|
||
if (!parse)
|
||
return null;
|
||
let oldVieport = parse.viewport;
|
||
parse.updateViewport({ from: 0, to: upto });
|
||
let result = parse.isDone(upto) || parse.work(timeout, upto) ? parse.tree : null;
|
||
parse.updateViewport(oldVieport);
|
||
return result;
|
||
}
|
||
function syntaxTreeAvailable(state, upto = state.doc.length) {
|
||
var _a2;
|
||
return ((_a2 = state.field(Language.state, false)) === null || _a2 === void 0 ? void 0 : _a2.context.isDone(upto)) || false;
|
||
}
|
||
function forceParsing(view, upto = view.viewport.to, timeout = 100) {
|
||
let success = ensureSyntaxTree(view.state, upto, timeout);
|
||
if (success != syntaxTree(view.state))
|
||
view.dispatch({});
|
||
return !!success;
|
||
}
|
||
function syntaxParserRunning(view) {
|
||
var _a2;
|
||
return ((_a2 = view.plugin(parseWorker)) === null || _a2 === void 0 ? void 0 : _a2.isWorking()) || false;
|
||
}
|
||
var DocInput = class {
|
||
/**
|
||
Create an input object for the given document.
|
||
*/
|
||
constructor(doc) {
|
||
this.doc = doc;
|
||
this.cursorPos = 0;
|
||
this.string = "";
|
||
this.cursor = doc.iter();
|
||
}
|
||
get length() {
|
||
return this.doc.length;
|
||
}
|
||
syncTo(pos) {
|
||
this.string = this.cursor.next(pos - this.cursorPos).value;
|
||
this.cursorPos = pos + this.string.length;
|
||
return this.cursorPos - this.string.length;
|
||
}
|
||
chunk(pos) {
|
||
this.syncTo(pos);
|
||
return this.string;
|
||
}
|
||
get lineChunks() {
|
||
return true;
|
||
}
|
||
read(from, to) {
|
||
let stringStart = this.cursorPos - this.string.length;
|
||
if (from < stringStart || to >= this.cursorPos)
|
||
return this.doc.sliceString(from, to);
|
||
else
|
||
return this.string.slice(from - stringStart, to - stringStart);
|
||
}
|
||
};
|
||
var currentContext = null;
|
||
var ParseContext = class _ParseContext {
|
||
constructor(parser, state, fragments = [], tree, treeLen, viewport, skipped, scheduleOn) {
|
||
this.parser = parser;
|
||
this.state = state;
|
||
this.fragments = fragments;
|
||
this.tree = tree;
|
||
this.treeLen = treeLen;
|
||
this.viewport = viewport;
|
||
this.skipped = skipped;
|
||
this.scheduleOn = scheduleOn;
|
||
this.parse = null;
|
||
this.tempSkipped = [];
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
static create(parser, state, viewport) {
|
||
return new _ParseContext(parser, state, [], Tree.empty, 0, viewport, [], null);
|
||
}
|
||
startParse() {
|
||
return this.parser.startParse(new DocInput(this.state.doc), this.fragments);
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
work(until, upto) {
|
||
if (upto != null && upto >= this.state.doc.length)
|
||
upto = void 0;
|
||
if (this.tree != Tree.empty && this.isDone(upto !== null && upto !== void 0 ? upto : this.state.doc.length)) {
|
||
this.takeTree();
|
||
return true;
|
||
}
|
||
return this.withContext(() => {
|
||
var _a2;
|
||
if (typeof until == "number") {
|
||
let endTime = Date.now() + until;
|
||
until = () => Date.now() > endTime;
|
||
}
|
||
if (!this.parse)
|
||
this.parse = this.startParse();
|
||
if (upto != null && (this.parse.stoppedAt == null || this.parse.stoppedAt > upto) && upto < this.state.doc.length)
|
||
this.parse.stopAt(upto);
|
||
for (; ; ) {
|
||
let done = this.parse.advance();
|
||
if (done) {
|
||
this.fragments = this.withoutTempSkipped(TreeFragment.addTree(done, this.fragments, this.parse.stoppedAt != null));
|
||
this.treeLen = (_a2 = this.parse.stoppedAt) !== null && _a2 !== void 0 ? _a2 : this.state.doc.length;
|
||
this.tree = done;
|
||
this.parse = null;
|
||
if (this.treeLen < (upto !== null && upto !== void 0 ? upto : this.state.doc.length))
|
||
this.parse = this.startParse();
|
||
else
|
||
return true;
|
||
}
|
||
if (until())
|
||
return false;
|
||
}
|
||
});
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
takeTree() {
|
||
let pos, tree;
|
||
if (this.parse && (pos = this.parse.parsedPos) >= this.treeLen) {
|
||
if (this.parse.stoppedAt == null || this.parse.stoppedAt > pos)
|
||
this.parse.stopAt(pos);
|
||
this.withContext(() => {
|
||
while (!(tree = this.parse.advance())) {
|
||
}
|
||
});
|
||
this.treeLen = pos;
|
||
this.tree = tree;
|
||
this.fragments = this.withoutTempSkipped(TreeFragment.addTree(this.tree, this.fragments, true));
|
||
this.parse = null;
|
||
}
|
||
}
|
||
withContext(f) {
|
||
let prev = currentContext;
|
||
currentContext = this;
|
||
try {
|
||
return f();
|
||
} finally {
|
||
currentContext = prev;
|
||
}
|
||
}
|
||
withoutTempSkipped(fragments) {
|
||
for (let r; r = this.tempSkipped.pop(); )
|
||
fragments = cutFragments(fragments, r.from, r.to);
|
||
return fragments;
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
changes(changes, newState) {
|
||
let { fragments, tree, treeLen, viewport, skipped } = this;
|
||
this.takeTree();
|
||
if (!changes.empty) {
|
||
let ranges = [];
|
||
changes.iterChangedRanges((fromA, toA, fromB, toB) => ranges.push({ fromA, toA, fromB, toB }));
|
||
fragments = TreeFragment.applyChanges(fragments, ranges);
|
||
tree = Tree.empty;
|
||
treeLen = 0;
|
||
viewport = { from: changes.mapPos(viewport.from, -1), to: changes.mapPos(viewport.to, 1) };
|
||
if (this.skipped.length) {
|
||
skipped = [];
|
||
for (let r of this.skipped) {
|
||
let from = changes.mapPos(r.from, 1), to = changes.mapPos(r.to, -1);
|
||
if (from < to)
|
||
skipped.push({ from, to });
|
||
}
|
||
}
|
||
}
|
||
return new _ParseContext(this.parser, newState, fragments, tree, treeLen, viewport, skipped, this.scheduleOn);
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
updateViewport(viewport) {
|
||
if (this.viewport.from == viewport.from && this.viewport.to == viewport.to)
|
||
return false;
|
||
this.viewport = viewport;
|
||
let startLen = this.skipped.length;
|
||
for (let i = 0; i < this.skipped.length; i++) {
|
||
let { from, to } = this.skipped[i];
|
||
if (from < viewport.to && to > viewport.from) {
|
||
this.fragments = cutFragments(this.fragments, from, to);
|
||
this.skipped.splice(i--, 1);
|
||
}
|
||
}
|
||
if (this.skipped.length >= startLen)
|
||
return false;
|
||
this.reset();
|
||
return true;
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
reset() {
|
||
if (this.parse) {
|
||
this.takeTree();
|
||
this.parse = null;
|
||
}
|
||
}
|
||
/**
|
||
Notify the parse scheduler that the given region was skipped
|
||
because it wasn't in view, and the parse should be restarted
|
||
when it comes into view.
|
||
*/
|
||
skipUntilInView(from, to) {
|
||
this.skipped.push({ from, to });
|
||
}
|
||
/**
|
||
Returns a parser intended to be used as placeholder when
|
||
asynchronously loading a nested parser. It'll skip its input and
|
||
mark it as not-really-parsed, so that the next update will parse
|
||
it again.
|
||
|
||
When `until` is given, a reparse will be scheduled when that
|
||
promise resolves.
|
||
*/
|
||
static getSkippingParser(until) {
|
||
return new class extends Parser {
|
||
createParse(input, fragments, ranges) {
|
||
let from = ranges[0].from, to = ranges[ranges.length - 1].to;
|
||
let parser = {
|
||
parsedPos: from,
|
||
advance() {
|
||
let cx = currentContext;
|
||
if (cx) {
|
||
for (let r of ranges)
|
||
cx.tempSkipped.push(r);
|
||
if (until)
|
||
cx.scheduleOn = cx.scheduleOn ? Promise.all([cx.scheduleOn, until]) : until;
|
||
}
|
||
this.parsedPos = to;
|
||
return new Tree(NodeType.none, [], [], to - from);
|
||
},
|
||
stoppedAt: null,
|
||
stopAt() {
|
||
}
|
||
};
|
||
return parser;
|
||
}
|
||
}();
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
isDone(upto) {
|
||
upto = Math.min(upto, this.state.doc.length);
|
||
let frags = this.fragments;
|
||
return this.treeLen >= upto && frags.length && frags[0].from == 0 && frags[0].to >= upto;
|
||
}
|
||
/**
|
||
Get the context for the current parse, or `null` if no editor
|
||
parse is in progress.
|
||
*/
|
||
static get() {
|
||
return currentContext;
|
||
}
|
||
};
|
||
function cutFragments(fragments, from, to) {
|
||
return TreeFragment.applyChanges(fragments, [{ fromA: from, toA: to, fromB: from, toB: to }]);
|
||
}
|
||
var LanguageState = class _LanguageState {
|
||
constructor(context) {
|
||
this.context = context;
|
||
this.tree = context.tree;
|
||
}
|
||
apply(tr) {
|
||
if (!tr.docChanged && this.tree == this.context.tree)
|
||
return this;
|
||
let newCx = this.context.changes(tr.changes, tr.state);
|
||
let upto = this.context.treeLen == tr.startState.doc.length ? void 0 : Math.max(tr.changes.mapPos(this.context.treeLen), newCx.viewport.to);
|
||
if (!newCx.work(20, upto))
|
||
newCx.takeTree();
|
||
return new _LanguageState(newCx);
|
||
}
|
||
static init(state) {
|
||
let vpTo = Math.min(3e3, state.doc.length);
|
||
let parseState = ParseContext.create(state.facet(language).parser, state, { from: 0, to: vpTo });
|
||
if (!parseState.work(20, vpTo))
|
||
parseState.takeTree();
|
||
return new _LanguageState(parseState);
|
||
}
|
||
};
|
||
Language.state = StateField.define({
|
||
create: LanguageState.init,
|
||
update(value, tr) {
|
||
for (let e of tr.effects)
|
||
if (e.is(Language.setState))
|
||
return e.value;
|
||
if (tr.startState.facet(language) != tr.state.facet(language))
|
||
return LanguageState.init(tr.state);
|
||
return value.apply(tr);
|
||
}
|
||
});
|
||
var requestIdle = (callback) => {
|
||
let timeout = setTimeout(
|
||
() => callback(),
|
||
500
|
||
/* MaxPause */
|
||
);
|
||
return () => clearTimeout(timeout);
|
||
};
|
||
if (typeof requestIdleCallback != "undefined")
|
||
requestIdle = (callback) => {
|
||
let idle = -1, timeout = setTimeout(
|
||
() => {
|
||
idle = requestIdleCallback(callback, {
|
||
timeout: 500 - 100
|
||
/* MinPause */
|
||
});
|
||
},
|
||
100
|
||
/* MinPause */
|
||
);
|
||
return () => idle < 0 ? clearTimeout(timeout) : cancelIdleCallback(idle);
|
||
};
|
||
var isInputPending = typeof navigator != "undefined" && ((_a = navigator.scheduling) === null || _a === void 0 ? void 0 : _a.isInputPending) ? () => navigator.scheduling.isInputPending() : null;
|
||
var parseWorker = ViewPlugin.fromClass(class ParseWorker {
|
||
constructor(view) {
|
||
this.view = view;
|
||
this.working = null;
|
||
this.workScheduled = 0;
|
||
this.chunkEnd = -1;
|
||
this.chunkBudget = -1;
|
||
this.work = this.work.bind(this);
|
||
this.scheduleWork();
|
||
}
|
||
update(update) {
|
||
let cx = this.view.state.field(Language.state).context;
|
||
if (cx.updateViewport(update.view.viewport) || this.view.viewport.to > cx.treeLen)
|
||
this.scheduleWork();
|
||
if (update.docChanged) {
|
||
if (this.view.hasFocus)
|
||
this.chunkBudget += 50;
|
||
this.scheduleWork();
|
||
}
|
||
this.checkAsyncSchedule(cx);
|
||
}
|
||
scheduleWork() {
|
||
if (this.working)
|
||
return;
|
||
let { state } = this.view, field = state.field(Language.state);
|
||
if (field.tree != field.context.tree || !field.context.isDone(state.doc.length))
|
||
this.working = requestIdle(this.work);
|
||
}
|
||
work(deadline) {
|
||
this.working = null;
|
||
let now = Date.now();
|
||
if (this.chunkEnd < now && (this.chunkEnd < 0 || this.view.hasFocus)) {
|
||
this.chunkEnd = now + 3e4;
|
||
this.chunkBudget = 3e3;
|
||
}
|
||
if (this.chunkBudget <= 0)
|
||
return;
|
||
let { state, viewport: { to: vpTo } } = this.view, field = state.field(Language.state);
|
||
if (field.tree == field.context.tree && field.context.isDone(
|
||
vpTo + 1e5
|
||
/* MaxParseAhead */
|
||
))
|
||
return;
|
||
let endTime = Date.now() + Math.min(this.chunkBudget, 100, deadline && !isInputPending ? Math.max(25, deadline.timeRemaining() - 5) : 1e9);
|
||
let viewportFirst = field.context.treeLen < vpTo && state.doc.length > vpTo + 1e3;
|
||
let done = field.context.work(() => {
|
||
return isInputPending && isInputPending() || Date.now() > endTime;
|
||
}, vpTo + (viewportFirst ? 0 : 1e5));
|
||
this.chunkBudget -= Date.now() - now;
|
||
if (done || this.chunkBudget <= 0) {
|
||
field.context.takeTree();
|
||
this.view.dispatch({ effects: Language.setState.of(new LanguageState(field.context)) });
|
||
}
|
||
if (this.chunkBudget > 0 && !(done && !viewportFirst))
|
||
this.scheduleWork();
|
||
this.checkAsyncSchedule(field.context);
|
||
}
|
||
checkAsyncSchedule(cx) {
|
||
if (cx.scheduleOn) {
|
||
this.workScheduled++;
|
||
cx.scheduleOn.then(() => this.scheduleWork()).catch((err) => logException(this.view.state, err)).then(() => this.workScheduled--);
|
||
cx.scheduleOn = null;
|
||
}
|
||
}
|
||
destroy() {
|
||
if (this.working)
|
||
this.working();
|
||
}
|
||
isWorking() {
|
||
return !!(this.working || this.workScheduled > 0);
|
||
}
|
||
}, {
|
||
eventHandlers: { focus() {
|
||
this.scheduleWork();
|
||
} }
|
||
});
|
||
var language = Facet.define({
|
||
combine(languages) {
|
||
return languages.length ? languages[0] : null;
|
||
},
|
||
enables: (language2) => [
|
||
Language.state,
|
||
parseWorker,
|
||
EditorView.contentAttributes.compute([language2], (state) => {
|
||
let lang = state.facet(language2);
|
||
return lang && lang.name ? { "data-language": lang.name } : {};
|
||
})
|
||
]
|
||
});
|
||
var LanguageSupport = class {
|
||
/**
|
||
Create a language support object.
|
||
*/
|
||
constructor(language2, support = []) {
|
||
this.language = language2;
|
||
this.support = support;
|
||
this.extension = [language2, support];
|
||
}
|
||
};
|
||
var LanguageDescription = class _LanguageDescription {
|
||
constructor(name, alias, extensions, filename, loadFunc, support = void 0) {
|
||
this.name = name;
|
||
this.alias = alias;
|
||
this.extensions = extensions;
|
||
this.filename = filename;
|
||
this.loadFunc = loadFunc;
|
||
this.support = support;
|
||
this.loading = null;
|
||
}
|
||
/**
|
||
Start loading the the language. Will return a promise that
|
||
resolves to a [`LanguageSupport`](https://codemirror.net/6/docs/ref/#language.LanguageSupport)
|
||
object when the language successfully loads.
|
||
*/
|
||
load() {
|
||
return this.loading || (this.loading = this.loadFunc().then((support) => this.support = support, (err) => {
|
||
this.loading = null;
|
||
throw err;
|
||
}));
|
||
}
|
||
/**
|
||
Create a language description.
|
||
*/
|
||
static of(spec) {
|
||
let { load, support } = spec;
|
||
if (!load) {
|
||
if (!support)
|
||
throw new RangeError("Must pass either 'load' or 'support' to LanguageDescription.of");
|
||
load = () => Promise.resolve(support);
|
||
}
|
||
return new _LanguageDescription(spec.name, (spec.alias || []).concat(spec.name).map((s) => s.toLowerCase()), spec.extensions || [], spec.filename, load, support);
|
||
}
|
||
/**
|
||
Look for a language in the given array of descriptions that
|
||
matches the filename. Will first match
|
||
[`filename`](https://codemirror.net/6/docs/ref/#language.LanguageDescription.filename) patterns,
|
||
and then [extensions](https://codemirror.net/6/docs/ref/#language.LanguageDescription.extensions),
|
||
and return the first language that matches.
|
||
*/
|
||
static matchFilename(descs, filename) {
|
||
for (let d of descs)
|
||
if (d.filename && d.filename.test(filename))
|
||
return d;
|
||
let ext = /\.([^.]+)$/.exec(filename);
|
||
if (ext) {
|
||
for (let d of descs)
|
||
if (d.extensions.indexOf(ext[1]) > -1)
|
||
return d;
|
||
}
|
||
return null;
|
||
}
|
||
/**
|
||
Look for a language whose name or alias matches the the given
|
||
name (case-insensitively). If `fuzzy` is true, and no direct
|
||
matchs is found, this'll also search for a language whose name
|
||
or alias occurs in the string (for names shorter than three
|
||
characters, only when surrounded by non-word characters).
|
||
*/
|
||
static matchLanguageName(descs, name, fuzzy = true) {
|
||
name = name.toLowerCase();
|
||
for (let d of descs)
|
||
if (d.alias.some((a) => a == name))
|
||
return d;
|
||
if (fuzzy)
|
||
for (let d of descs)
|
||
for (let a of d.alias) {
|
||
let found = name.indexOf(a);
|
||
if (found > -1 && (a.length > 2 || !/\w/.test(name[found - 1]) && !/\w/.test(name[found + a.length])))
|
||
return d;
|
||
}
|
||
return null;
|
||
}
|
||
};
|
||
var indentService = Facet.define();
|
||
var indentUnit = Facet.define({
|
||
combine: (values) => {
|
||
if (!values.length)
|
||
return " ";
|
||
let unit = values[0];
|
||
if (!unit || /\S/.test(unit) || Array.from(unit).some((e) => e != unit[0]))
|
||
throw new Error("Invalid indent unit: " + JSON.stringify(values[0]));
|
||
return unit;
|
||
}
|
||
});
|
||
function getIndentUnit(state) {
|
||
let unit = state.facet(indentUnit);
|
||
return unit.charCodeAt(0) == 9 ? state.tabSize * unit.length : unit.length;
|
||
}
|
||
function indentString(state, cols) {
|
||
let result = "", ts = state.tabSize, ch = state.facet(indentUnit)[0];
|
||
if (ch == " ") {
|
||
while (cols >= ts) {
|
||
result += " ";
|
||
cols -= ts;
|
||
}
|
||
ch = " ";
|
||
}
|
||
for (let i = 0; i < cols; i++)
|
||
result += ch;
|
||
return result;
|
||
}
|
||
function getIndentation(context, pos) {
|
||
if (context instanceof EditorState)
|
||
context = new IndentContext(context);
|
||
for (let service of context.state.facet(indentService)) {
|
||
let result = service(context, pos);
|
||
if (result !== void 0)
|
||
return result;
|
||
}
|
||
let tree = syntaxTree(context.state);
|
||
return tree ? syntaxIndentation(context, tree, pos) : null;
|
||
}
|
||
function indentRange(state, from, to) {
|
||
let updated = /* @__PURE__ */ Object.create(null);
|
||
let context = new IndentContext(state, { overrideIndentation: (start) => {
|
||
var _a2;
|
||
return (_a2 = updated[start]) !== null && _a2 !== void 0 ? _a2 : -1;
|
||
} });
|
||
let changes = [];
|
||
for (let pos = from; pos <= to; ) {
|
||
let line = state.doc.lineAt(pos);
|
||
pos = line.to + 1;
|
||
let indent = getIndentation(context, line.from);
|
||
if (indent == null)
|
||
continue;
|
||
if (!/\S/.test(line.text))
|
||
indent = 0;
|
||
let cur = /^\s*/.exec(line.text)[0];
|
||
let norm = indentString(state, indent);
|
||
if (cur != norm) {
|
||
updated[line.from] = indent;
|
||
changes.push({ from: line.from, to: line.from + cur.length, insert: norm });
|
||
}
|
||
}
|
||
return state.changes(changes);
|
||
}
|
||
var IndentContext = class {
|
||
/**
|
||
Create an indent context.
|
||
*/
|
||
constructor(state, options = {}) {
|
||
this.state = state;
|
||
this.options = options;
|
||
this.unit = getIndentUnit(state);
|
||
}
|
||
/**
|
||
Get a description of the line at the given position, taking
|
||
[simulated line
|
||
breaks](https://codemirror.net/6/docs/ref/#language.IndentContext.constructor^options.simulateBreak)
|
||
into account. If there is such a break at `pos`, the `bias`
|
||
argument determines whether the part of the line line before or
|
||
after the break is used.
|
||
*/
|
||
lineAt(pos, bias = 1) {
|
||
let line = this.state.doc.lineAt(pos);
|
||
let { simulateBreak, simulateDoubleBreak } = this.options;
|
||
if (simulateBreak != null && simulateBreak >= line.from && simulateBreak <= line.to) {
|
||
if (simulateDoubleBreak && simulateBreak == pos)
|
||
return { text: "", from: pos };
|
||
else if (bias < 0 ? simulateBreak < pos : simulateBreak <= pos)
|
||
return { text: line.text.slice(simulateBreak - line.from), from: simulateBreak };
|
||
else
|
||
return { text: line.text.slice(0, simulateBreak - line.from), from: line.from };
|
||
}
|
||
return line;
|
||
}
|
||
/**
|
||
Get the text directly after `pos`, either the entire line
|
||
or the next 100 characters, whichever is shorter.
|
||
*/
|
||
textAfterPos(pos, bias = 1) {
|
||
if (this.options.simulateDoubleBreak && pos == this.options.simulateBreak)
|
||
return "";
|
||
let { text, from } = this.lineAt(pos, bias);
|
||
return text.slice(pos - from, Math.min(text.length, pos + 100 - from));
|
||
}
|
||
/**
|
||
Find the column for the given position.
|
||
*/
|
||
column(pos, bias = 1) {
|
||
let { text, from } = this.lineAt(pos, bias);
|
||
let result = this.countColumn(text, pos - from);
|
||
let override = this.options.overrideIndentation ? this.options.overrideIndentation(from) : -1;
|
||
if (override > -1)
|
||
result += override - this.countColumn(text, text.search(/\S|$/));
|
||
return result;
|
||
}
|
||
/**
|
||
Find the column position (taking tabs into account) of the given
|
||
position in the given string.
|
||
*/
|
||
countColumn(line, pos = line.length) {
|
||
return countColumn(line, this.state.tabSize, pos);
|
||
}
|
||
/**
|
||
Find the indentation column of the line at the given point.
|
||
*/
|
||
lineIndent(pos, bias = 1) {
|
||
let { text, from } = this.lineAt(pos, bias);
|
||
let override = this.options.overrideIndentation;
|
||
if (override) {
|
||
let overriden = override(from);
|
||
if (overriden > -1)
|
||
return overriden;
|
||
}
|
||
return this.countColumn(text, text.search(/\S|$/));
|
||
}
|
||
/**
|
||
Returns the [simulated line
|
||
break](https://codemirror.net/6/docs/ref/#language.IndentContext.constructor^options.simulateBreak)
|
||
for this context, if any.
|
||
*/
|
||
get simulatedBreak() {
|
||
return this.options.simulateBreak || null;
|
||
}
|
||
};
|
||
var indentNodeProp = new NodeProp();
|
||
function syntaxIndentation(cx, ast, pos) {
|
||
return indentFrom(ast.resolveInner(pos).enterUnfinishedNodesBefore(pos), pos, cx);
|
||
}
|
||
function ignoreClosed(cx) {
|
||
return cx.pos == cx.options.simulateBreak && cx.options.simulateDoubleBreak;
|
||
}
|
||
function indentStrategy(tree) {
|
||
let strategy = tree.type.prop(indentNodeProp);
|
||
if (strategy)
|
||
return strategy;
|
||
let first = tree.firstChild, close;
|
||
if (first && (close = first.type.prop(NodeProp.closedBy))) {
|
||
let last = tree.lastChild, closed = last && close.indexOf(last.name) > -1;
|
||
return (cx) => delimitedStrategy(cx, true, 1, void 0, closed && !ignoreClosed(cx) ? last.from : void 0);
|
||
}
|
||
return tree.parent == null ? topIndent : null;
|
||
}
|
||
function indentFrom(node, pos, base) {
|
||
for (; node; node = node.parent) {
|
||
let strategy = indentStrategy(node);
|
||
if (strategy)
|
||
return strategy(TreeIndentContext.create(base, pos, node));
|
||
}
|
||
return null;
|
||
}
|
||
function topIndent() {
|
||
return 0;
|
||
}
|
||
var TreeIndentContext = class _TreeIndentContext extends IndentContext {
|
||
constructor(base, pos, node) {
|
||
super(base.state, base.options);
|
||
this.base = base;
|
||
this.pos = pos;
|
||
this.node = node;
|
||
}
|
||
/**
|
||
@internal
|
||
*/
|
||
static create(base, pos, node) {
|
||
return new _TreeIndentContext(base, pos, node);
|
||
}
|
||
/**
|
||
Get the text directly after `this.pos`, either the entire line
|
||
or the next 100 characters, whichever is shorter.
|
||
*/
|
||
get textAfter() {
|
||
return this.textAfterPos(this.pos);
|
||
}
|
||
/**
|
||
Get the indentation at the reference line for `this.node`, which
|
||
is the line on which it starts, unless there is a node that is
|
||
_not_ a parent of this node covering the start of that line. If
|
||
so, the line at the start of that node is tried, again skipping
|
||
on if it is covered by another such node.
|
||
*/
|
||
get baseIndent() {
|
||
return this.baseIndentFor(this.node);
|
||
}
|
||
/**
|
||
Get the indentation for the reference line of the given node
|
||
(see [`baseIndent`](https://codemirror.net/6/docs/ref/#language.TreeIndentContext.baseIndent)).
|
||
*/
|
||
baseIndentFor(node) {
|
||
let line = this.state.doc.lineAt(node.from);
|
||
for (; ; ) {
|
||
let atBreak = node.resolve(line.from);
|
||
while (atBreak.parent && atBreak.parent.from == atBreak.from)
|
||
atBreak = atBreak.parent;
|
||
if (isParent(atBreak, node))
|
||
break;
|
||
line = this.state.doc.lineAt(atBreak.from);
|
||
}
|
||
return this.lineIndent(line.from);
|
||
}
|
||
/**
|
||
Continue looking for indentations in the node's parent nodes,
|
||
and return the result of that.
|
||
*/
|
||
continue() {
|
||
let parent = this.node.parent;
|
||
return parent ? indentFrom(parent, this.pos, this.base) : 0;
|
||
}
|
||
};
|
||
function isParent(parent, of) {
|
||
for (let cur = of; cur; cur = cur.parent)
|
||
if (parent == cur)
|
||
return true;
|
||
return false;
|
||
}
|
||
function bracketedAligned(context) {
|
||
let tree = context.node;
|
||
let openToken = tree.childAfter(tree.from), last = tree.lastChild;
|
||
if (!openToken)
|
||
return null;
|
||
let sim = context.options.simulateBreak;
|
||
let openLine = context.state.doc.lineAt(openToken.from);
|
||
let lineEnd = sim == null || sim <= openLine.from ? openLine.to : Math.min(openLine.to, sim);
|
||
for (let pos = openToken.to; ; ) {
|
||
let next = tree.childAfter(pos);
|
||
if (!next || next == last)
|
||
return null;
|
||
if (!next.type.isSkipped)
|
||
return next.from < lineEnd ? openToken : null;
|
||
pos = next.to;
|
||
}
|
||
}
|
||
function delimitedIndent({ closing, align = true, units = 1 }) {
|
||
return (context) => delimitedStrategy(context, align, units, closing);
|
||
}
|
||
function delimitedStrategy(context, align, units, closing, closedAt) {
|
||
let after = context.textAfter, space = after.match(/^\s*/)[0].length;
|
||
let closed = closing && after.slice(space, space + closing.length) == closing || closedAt == context.pos + space;
|
||
let aligned = align ? bracketedAligned(context) : null;
|
||
if (aligned)
|
||
return closed ? context.column(aligned.from) : context.column(aligned.to);
|
||
return context.baseIndent + (closed ? 0 : context.unit * units);
|
||
}
|
||
var flatIndent = (context) => context.baseIndent;
|
||
function continuedIndent({ except, units = 1 } = {}) {
|
||
return (context) => {
|
||
let matchExcept = except && except.test(context.textAfter);
|
||
return context.baseIndent + (matchExcept ? 0 : units * context.unit);
|
||
};
|
||
}
|
||
var DontIndentBeyond = 200;
|
||
function indentOnInput() {
|
||
return EditorState.transactionFilter.of((tr) => {
|
||
if (!tr.docChanged || !tr.isUserEvent("input.type") && !tr.isUserEvent("input.complete"))
|
||
return tr;
|
||
let rules = tr.startState.languageDataAt("indentOnInput", tr.startState.selection.main.head);
|
||
if (!rules.length)
|
||
return tr;
|
||
let doc = tr.newDoc, { head } = tr.newSelection.main, line = doc.lineAt(head);
|
||
if (head > line.from + DontIndentBeyond)
|
||
return tr;
|
||
let lineStart = doc.sliceString(line.from, head);
|
||
if (!rules.some((r) => r.test(lineStart)))
|
||
return tr;
|
||
let { state } = tr, last = -1, changes = [];
|
||
for (let { head: head2 } of state.selection.ranges) {
|
||
let line2 = state.doc.lineAt(head2);
|
||
if (line2.from == last)
|
||
continue;
|
||
last = line2.from;
|
||
let indent = getIndentation(state, line2.from);
|
||
if (indent == null)
|
||
continue;
|
||
let cur = /^\s*/.exec(line2.text)[0];
|
||
let norm = indentString(state, indent);
|
||
if (cur != norm)
|
||
changes.push({ from: line2.from, to: line2.from + cur.length, insert: norm });
|
||
}
|
||
return changes.length ? [tr, { changes, sequential: true }] : tr;
|
||
});
|
||
}
|
||
var foldService = Facet.define();
|
||
var foldNodeProp = new NodeProp();
|
||
function foldInside(node) {
|
||
let first = node.firstChild, last = node.lastChild;
|
||
return first && first.to < last.from ? { from: first.to, to: last.type.isError ? node.to : last.from } : null;
|
||
}
|
||
function syntaxFolding(state, start, end) {
|
||
let tree = syntaxTree(state);
|
||
if (tree.length < end)
|
||
return null;
|
||
let inner = tree.resolveInner(end, 1);
|
||
let found = null;
|
||
for (let cur = inner; cur; cur = cur.parent) {
|
||
if (cur.to <= end || cur.from > end)
|
||
continue;
|
||
if (found && cur.from < start)
|
||
break;
|
||
let prop = cur.type.prop(foldNodeProp);
|
||
if (prop && (cur.to < tree.length - 50 || tree.length == state.doc.length || !isUnfinished(cur))) {
|
||
let value = prop(cur, state);
|
||
if (value && value.from <= end && value.from >= start && value.to > end)
|
||
found = value;
|
||
}
|
||
}
|
||
return found;
|
||
}
|
||
function isUnfinished(node) {
|
||
let ch = node.lastChild;
|
||
return ch && ch.to == node.to && ch.type.isError;
|
||
}
|
||
function foldable(state, lineStart, lineEnd) {
|
||
for (let service of state.facet(foldService)) {
|
||
let result = service(state, lineStart, lineEnd);
|
||
if (result)
|
||
return result;
|
||
}
|
||
return syntaxFolding(state, lineStart, lineEnd);
|
||
}
|
||
function mapRange(range, mapping) {
|
||
let from = mapping.mapPos(range.from, 1), to = mapping.mapPos(range.to, -1);
|
||
return from >= to ? void 0 : { from, to };
|
||
}
|
||
var foldEffect = StateEffect.define({ map: mapRange });
|
||
var unfoldEffect = StateEffect.define({ map: mapRange });
|
||
function selectedLines(view) {
|
||
let lines = [];
|
||
for (let { head } of view.state.selection.ranges) {
|
||
if (lines.some((l) => l.from <= head && l.to >= head))
|
||
continue;
|
||
lines.push(view.lineBlockAt(head));
|
||
}
|
||
return lines;
|
||
}
|
||
var foldState = StateField.define({
|
||
create() {
|
||
return Decoration.none;
|
||
},
|
||
update(folded, tr) {
|
||
folded = folded.map(tr.changes);
|
||
for (let e of tr.effects) {
|
||
if (e.is(foldEffect) && !foldExists(folded, e.value.from, e.value.to))
|
||
folded = folded.update({ add: [foldWidget.range(e.value.from, e.value.to)] });
|
||
else if (e.is(unfoldEffect))
|
||
folded = folded.update({
|
||
filter: (from, to) => e.value.from != from || e.value.to != to,
|
||
filterFrom: e.value.from,
|
||
filterTo: e.value.to
|
||
});
|
||
}
|
||
if (tr.selection) {
|
||
let onSelection = false, { head } = tr.selection.main;
|
||
folded.between(head, head, (a, b) => {
|
||
if (a < head && b > head)
|
||
onSelection = true;
|
||
});
|
||
if (onSelection)
|
||
folded = folded.update({
|
||
filterFrom: head,
|
||
filterTo: head,
|
||
filter: (a, b) => b <= head || a >= head
|
||
});
|
||
}
|
||
return folded;
|
||
},
|
||
provide: (f) => EditorView.decorations.from(f),
|
||
toJSON(folded, state) {
|
||
let ranges = [];
|
||
folded.between(0, state.doc.length, (from, to) => {
|
||
ranges.push(from, to);
|
||
});
|
||
return ranges;
|
||
},
|
||
fromJSON(value) {
|
||
if (!Array.isArray(value) || value.length % 2)
|
||
throw new RangeError("Invalid JSON for fold state");
|
||
let ranges = [];
|
||
for (let i = 0; i < value.length; ) {
|
||
let from = value[i++], to = value[i++];
|
||
if (typeof from != "number" || typeof to != "number")
|
||
throw new RangeError("Invalid JSON for fold state");
|
||
ranges.push(foldWidget.range(from, to));
|
||
}
|
||
return Decoration.set(ranges, true);
|
||
}
|
||
});
|
||
function foldedRanges(state) {
|
||
return state.field(foldState, false) || RangeSet.empty;
|
||
}
|
||
function findFold(state, from, to) {
|
||
var _a2;
|
||
let found = null;
|
||
(_a2 = state.field(foldState, false)) === null || _a2 === void 0 ? void 0 : _a2.between(from, to, (from2, to2) => {
|
||
if (!found || found.from > from2)
|
||
found = { from: from2, to: to2 };
|
||
});
|
||
return found;
|
||
}
|
||
function foldExists(folded, from, to) {
|
||
let found = false;
|
||
folded.between(from, from, (a, b) => {
|
||
if (a == from && b == to)
|
||
found = true;
|
||
});
|
||
return found;
|
||
}
|
||
function maybeEnable(state, other) {
|
||
return state.field(foldState, false) ? other : other.concat(StateEffect.appendConfig.of(codeFolding()));
|
||
}
|
||
var foldCode = (view) => {
|
||
for (let line of selectedLines(view)) {
|
||
let range = foldable(view.state, line.from, line.to);
|
||
if (range) {
|
||
view.dispatch({ effects: maybeEnable(view.state, [foldEffect.of(range), announceFold(view, range)]) });
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
};
|
||
var unfoldCode = (view) => {
|
||
if (!view.state.field(foldState, false))
|
||
return false;
|
||
let effects = [];
|
||
for (let line of selectedLines(view)) {
|
||
let folded = findFold(view.state, line.from, line.to);
|
||
if (folded)
|
||
effects.push(unfoldEffect.of(folded), announceFold(view, folded, false));
|
||
}
|
||
if (effects.length)
|
||
view.dispatch({ effects });
|
||
return effects.length > 0;
|
||
};
|
||
function announceFold(view, range, fold = true) {
|
||
let lineFrom = view.state.doc.lineAt(range.from).number, lineTo = view.state.doc.lineAt(range.to).number;
|
||
return EditorView.announce.of(`${view.state.phrase(fold ? "Folded lines" : "Unfolded lines")} ${lineFrom} ${view.state.phrase("to")} ${lineTo}.`);
|
||
}
|
||
var foldAll = (view) => {
|
||
let { state } = view, effects = [];
|
||
for (let pos = 0; pos < state.doc.length; ) {
|
||
let line = view.lineBlockAt(pos), range = foldable(state, line.from, line.to);
|
||
if (range)
|
||
effects.push(foldEffect.of(range));
|
||
pos = (range ? view.lineBlockAt(range.to) : line).to + 1;
|
||
}
|
||
if (effects.length)
|
||
view.dispatch({ effects: maybeEnable(view.state, effects) });
|
||
return !!effects.length;
|
||
};
|
||
var unfoldAll = (view) => {
|
||
let field = view.state.field(foldState, false);
|
||
if (!field || !field.size)
|
||
return false;
|
||
let effects = [];
|
||
field.between(0, view.state.doc.length, (from, to) => {
|
||
effects.push(unfoldEffect.of({ from, to }));
|
||
});
|
||
view.dispatch({ effects });
|
||
return true;
|
||
};
|
||
function foldableContainer(view, lineBlock) {
|
||
for (let line = lineBlock; ; ) {
|
||
let foldableRegion = foldable(view.state, line.from, line.to);
|
||
if (foldableRegion && foldableRegion.to > lineBlock.from)
|
||
return foldableRegion;
|
||
if (!line.from)
|
||
return null;
|
||
line = view.lineBlockAt(line.from - 1);
|
||
}
|
||
}
|
||
var toggleFold = (view) => {
|
||
let effects = [];
|
||
for (let line of selectedLines(view)) {
|
||
let folded = findFold(view.state, line.from, line.to);
|
||
if (folded) {
|
||
effects.push(unfoldEffect.of(folded), announceFold(view, folded, false));
|
||
} else {
|
||
let foldRange = foldableContainer(view, line);
|
||
if (foldRange)
|
||
effects.push(foldEffect.of(foldRange), announceFold(view, foldRange));
|
||
}
|
||
}
|
||
if (effects.length > 0)
|
||
view.dispatch({ effects: maybeEnable(view.state, effects) });
|
||
return !!effects.length;
|
||
};
|
||
var foldKeymap = [
|
||
{ key: "Ctrl-Shift-[", mac: "Cmd-Alt-[", run: foldCode },
|
||
{ key: "Ctrl-Shift-]", mac: "Cmd-Alt-]", run: unfoldCode },
|
||
{ key: "Ctrl-Alt-[", run: foldAll },
|
||
{ key: "Ctrl-Alt-]", run: unfoldAll }
|
||
];
|
||
var defaultConfig = {
|
||
placeholderDOM: null,
|
||
placeholderText: "…"
|
||
};
|
||
var foldConfig = Facet.define({
|
||
combine(values) {
|
||
return combineConfig(values, defaultConfig);
|
||
}
|
||
});
|
||
function codeFolding(config) {
|
||
let result = [foldState, baseTheme$1];
|
||
if (config)
|
||
result.push(foldConfig.of(config));
|
||
return result;
|
||
}
|
||
var foldWidget = Decoration.replace({ widget: new class extends WidgetType {
|
||
toDOM(view) {
|
||
let { state } = view, conf = state.facet(foldConfig);
|
||
let onclick = (event) => {
|
||
let line = view.lineBlockAt(view.posAtDOM(event.target));
|
||
let folded = findFold(view.state, line.from, line.to);
|
||
if (folded)
|
||
view.dispatch({ effects: unfoldEffect.of(folded) });
|
||
event.preventDefault();
|
||
};
|
||
if (conf.placeholderDOM)
|
||
return conf.placeholderDOM(view, onclick);
|
||
let element = document.createElement("span");
|
||
element.textContent = conf.placeholderText;
|
||
element.setAttribute("aria-label", state.phrase("folded code"));
|
||
element.title = state.phrase("unfold");
|
||
element.className = "cm-foldPlaceholder";
|
||
element.onclick = onclick;
|
||
return element;
|
||
}
|
||
}() });
|
||
var foldGutterDefaults = {
|
||
openText: "⌄",
|
||
closedText: "›",
|
||
markerDOM: null,
|
||
domEventHandlers: {},
|
||
foldingChanged: () => false
|
||
};
|
||
var FoldMarker = class extends GutterMarker {
|
||
constructor(config, open) {
|
||
super();
|
||
this.config = config;
|
||
this.open = open;
|
||
}
|
||
eq(other) {
|
||
return this.config == other.config && this.open == other.open;
|
||
}
|
||
toDOM(view) {
|
||
if (this.config.markerDOM)
|
||
return this.config.markerDOM(this.open);
|
||
let span = document.createElement("span");
|
||
span.textContent = this.open ? this.config.openText : this.config.closedText;
|
||
span.title = view.state.phrase(this.open ? "Fold line" : "Unfold line");
|
||
return span;
|
||
}
|
||
};
|
||
function foldGutter(config = {}) {
|
||
let fullConfig = Object.assign(Object.assign({}, foldGutterDefaults), config);
|
||
let canFold = new FoldMarker(fullConfig, true), canUnfold = new FoldMarker(fullConfig, false);
|
||
let markers = ViewPlugin.fromClass(class {
|
||
constructor(view) {
|
||
this.from = view.viewport.from;
|
||
this.markers = this.buildMarkers(view);
|
||
}
|
||
update(update) {
|
||
if (update.docChanged || update.viewportChanged || update.startState.facet(language) != update.state.facet(language) || update.startState.field(foldState, false) != update.state.field(foldState, false) || syntaxTree(update.startState) != syntaxTree(update.state) || fullConfig.foldingChanged(update))
|
||
this.markers = this.buildMarkers(update.view);
|
||
}
|
||
buildMarkers(view) {
|
||
let builder = new RangeSetBuilder();
|
||
for (let line of view.viewportLineBlocks) {
|
||
let mark = findFold(view.state, line.from, line.to) ? canUnfold : foldable(view.state, line.from, line.to) ? canFold : null;
|
||
if (mark)
|
||
builder.add(line.from, line.from, mark);
|
||
}
|
||
return builder.finish();
|
||
}
|
||
});
|
||
let { domEventHandlers } = fullConfig;
|
||
return [
|
||
markers,
|
||
gutter({
|
||
class: "cm-foldGutter",
|
||
markers(view) {
|
||
var _a2;
|
||
return ((_a2 = view.plugin(markers)) === null || _a2 === void 0 ? void 0 : _a2.markers) || RangeSet.empty;
|
||
},
|
||
initialSpacer() {
|
||
return new FoldMarker(fullConfig, false);
|
||
},
|
||
domEventHandlers: Object.assign(Object.assign({}, domEventHandlers), { click: (view, line, event) => {
|
||
if (domEventHandlers.click && domEventHandlers.click(view, line, event))
|
||
return true;
|
||
let folded = findFold(view.state, line.from, line.to);
|
||
if (folded) {
|
||
view.dispatch({ effects: unfoldEffect.of(folded) });
|
||
return true;
|
||
}
|
||
let range = foldable(view.state, line.from, line.to);
|
||
if (range) {
|
||
view.dispatch({ effects: foldEffect.of(range) });
|
||
return true;
|
||
}
|
||
return false;
|
||
} })
|
||
}),
|
||
codeFolding()
|
||
];
|
||
}
|
||
var baseTheme$1 = EditorView.baseTheme({
|
||
".cm-foldPlaceholder": {
|
||
backgroundColor: "#eee",
|
||
border: "1px solid #ddd",
|
||
color: "#888",
|
||
borderRadius: ".2em",
|
||
margin: "0 1px",
|
||
padding: "0 1px",
|
||
cursor: "pointer"
|
||
},
|
||
".cm-foldGutter span": {
|
||
padding: "0 1px",
|
||
cursor: "pointer"
|
||
}
|
||
});
|
||
var HighlightStyle = class _HighlightStyle {
|
||
constructor(specs, options) {
|
||
this.specs = specs;
|
||
let modSpec;
|
||
function def(spec) {
|
||
let cls = StyleModule.newName();
|
||
(modSpec || (modSpec = /* @__PURE__ */ Object.create(null)))["." + cls] = spec;
|
||
return cls;
|
||
}
|
||
const all = typeof options.all == "string" ? options.all : options.all ? def(options.all) : void 0;
|
||
const scopeOpt = options.scope;
|
||
this.scope = scopeOpt instanceof Language ? (type) => type.prop(languageDataProp) == scopeOpt.data : scopeOpt ? (type) => type == scopeOpt : void 0;
|
||
this.style = tagHighlighter(specs.map((style) => ({
|
||
tag: style.tag,
|
||
class: style.class || def(Object.assign({}, style, { tag: null }))
|
||
})), {
|
||
all
|
||
}).style;
|
||
this.module = modSpec ? new StyleModule(modSpec) : null;
|
||
this.themeType = options.themeType;
|
||
}
|
||
/**
|
||
Create a highlighter style that associates the given styles to
|
||
the given tags. The specs must be objects that hold a style tag
|
||
or array of tags in their `tag` property, and either a single
|
||
`class` property providing a static CSS class (for highlighter
|
||
that rely on external styling), or a
|
||
[`style-mod`](https://github.com/marijnh/style-mod#documentation)-style
|
||
set of CSS properties (which define the styling for those tags).
|
||
|
||
The CSS rules created for a highlighter will be emitted in the
|
||
order of the spec's properties. That means that for elements that
|
||
have multiple tags associated with them, styles defined further
|
||
down in the list will have a higher CSS precedence than styles
|
||
defined earlier.
|
||
*/
|
||
static define(specs, options) {
|
||
return new _HighlightStyle(specs, options || {});
|
||
}
|
||
};
|
||
var highlighterFacet = Facet.define();
|
||
var fallbackHighlighter = Facet.define({
|
||
combine(values) {
|
||
return values.length ? [values[0]] : null;
|
||
}
|
||
});
|
||
function getHighlighters(state) {
|
||
let main = state.facet(highlighterFacet);
|
||
return main.length ? main : state.facet(fallbackHighlighter);
|
||
}
|
||
function syntaxHighlighting(highlighter, options) {
|
||
let ext = [treeHighlighter], themeType;
|
||
if (highlighter instanceof HighlightStyle) {
|
||
if (highlighter.module)
|
||
ext.push(EditorView.styleModule.of(highlighter.module));
|
||
themeType = highlighter.themeType;
|
||
}
|
||
if (options === null || options === void 0 ? void 0 : options.fallback)
|
||
ext.push(fallbackHighlighter.of(highlighter));
|
||
else if (themeType)
|
||
ext.push(highlighterFacet.computeN([EditorView.darkTheme], (state) => {
|
||
return state.facet(EditorView.darkTheme) == (themeType == "dark") ? [highlighter] : [];
|
||
}));
|
||
else
|
||
ext.push(highlighterFacet.of(highlighter));
|
||
return ext;
|
||
}
|
||
function highlightingFor(state, tags2, scope) {
|
||
let highlighters = getHighlighters(state);
|
||
let result = null;
|
||
if (highlighters)
|
||
for (let highlighter of highlighters) {
|
||
if (!highlighter.scope || scope && highlighter.scope(scope)) {
|
||
let cls = highlighter.style(tags2);
|
||
if (cls)
|
||
result = result ? result + " " + cls : cls;
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
var TreeHighlighter = class {
|
||
constructor(view) {
|
||
this.markCache = /* @__PURE__ */ Object.create(null);
|
||
this.tree = syntaxTree(view.state);
|
||
this.decorations = this.buildDeco(view, getHighlighters(view.state));
|
||
}
|
||
update(update) {
|
||
let tree = syntaxTree(update.state), highlighters = getHighlighters(update.state);
|
||
let styleChange = highlighters != getHighlighters(update.startState);
|
||
if (tree.length < update.view.viewport.to && !styleChange && tree.type == this.tree.type) {
|
||
this.decorations = this.decorations.map(update.changes);
|
||
} else if (tree != this.tree || update.viewportChanged || styleChange) {
|
||
this.tree = tree;
|
||
this.decorations = this.buildDeco(update.view, highlighters);
|
||
}
|
||
}
|
||
buildDeco(view, highlighters) {
|
||
if (!highlighters || !this.tree.length)
|
||
return Decoration.none;
|
||
let builder = new RangeSetBuilder();
|
||
for (let { from, to } of view.visibleRanges) {
|
||
highlightTree(this.tree, highlighters, (from2, to2, style) => {
|
||
builder.add(from2, to2, this.markCache[style] || (this.markCache[style] = Decoration.mark({ class: style })));
|
||
}, from, to);
|
||
}
|
||
return builder.finish();
|
||
}
|
||
};
|
||
var treeHighlighter = Prec.high(ViewPlugin.fromClass(TreeHighlighter, {
|
||
decorations: (v) => v.decorations
|
||
}));
|
||
var defaultHighlightStyle = HighlightStyle.define([
|
||
{
|
||
tag: tags.meta,
|
||
color: "#404740"
|
||
},
|
||
{
|
||
tag: tags.link,
|
||
textDecoration: "underline"
|
||
},
|
||
{
|
||
tag: tags.heading,
|
||
textDecoration: "underline",
|
||
fontWeight: "bold"
|
||
},
|
||
{
|
||
tag: tags.emphasis,
|
||
fontStyle: "italic"
|
||
},
|
||
{
|
||
tag: tags.strong,
|
||
fontWeight: "bold"
|
||
},
|
||
{
|
||
tag: tags.strikethrough,
|
||
textDecoration: "line-through"
|
||
},
|
||
{
|
||
tag: tags.keyword,
|
||
color: "#708"
|
||
},
|
||
{
|
||
tag: [tags.atom, tags.bool, tags.url, tags.contentSeparator, tags.labelName],
|
||
color: "#219"
|
||
},
|
||
{
|
||
tag: [tags.literal, tags.inserted],
|
||
color: "#164"
|
||
},
|
||
{
|
||
tag: [tags.string, tags.deleted],
|
||
color: "#a11"
|
||
},
|
||
{
|
||
tag: [tags.regexp, tags.escape, tags.special(tags.string)],
|
||
color: "#e40"
|
||
},
|
||
{
|
||
tag: tags.definition(tags.variableName),
|
||
color: "#00f"
|
||
},
|
||
{
|
||
tag: tags.local(tags.variableName),
|
||
color: "#30a"
|
||
},
|
||
{
|
||
tag: [tags.typeName, tags.namespace],
|
||
color: "#085"
|
||
},
|
||
{
|
||
tag: tags.className,
|
||
color: "#167"
|
||
},
|
||
{
|
||
tag: [tags.special(tags.variableName), tags.macroName],
|
||
color: "#256"
|
||
},
|
||
{
|
||
tag: tags.definition(tags.propertyName),
|
||
color: "#00c"
|
||
},
|
||
{
|
||
tag: tags.comment,
|
||
color: "#940"
|
||
},
|
||
{
|
||
tag: tags.invalid,
|
||
color: "#f00"
|
||
}
|
||
]);
|
||
var baseTheme = EditorView.baseTheme({
|
||
"&.cm-focused .cm-matchingBracket": { backgroundColor: "#328c8252" },
|
||
"&.cm-focused .cm-nonmatchingBracket": { backgroundColor: "#bb555544" }
|
||
});
|
||
var DefaultScanDist = 1e4;
|
||
var DefaultBrackets = "()[]{}";
|
||
var bracketMatchingConfig = Facet.define({
|
||
combine(configs) {
|
||
return combineConfig(configs, {
|
||
afterCursor: true,
|
||
brackets: DefaultBrackets,
|
||
maxScanDistance: DefaultScanDist,
|
||
renderMatch: defaultRenderMatch
|
||
});
|
||
}
|
||
});
|
||
var matchingMark = Decoration.mark({ class: "cm-matchingBracket" });
|
||
var nonmatchingMark = Decoration.mark({ class: "cm-nonmatchingBracket" });
|
||
function defaultRenderMatch(match) {
|
||
let decorations = [];
|
||
let mark = match.matched ? matchingMark : nonmatchingMark;
|
||
decorations.push(mark.range(match.start.from, match.start.to));
|
||
if (match.end)
|
||
decorations.push(mark.range(match.end.from, match.end.to));
|
||
return decorations;
|
||
}
|
||
var bracketMatchingState = StateField.define({
|
||
create() {
|
||
return Decoration.none;
|
||
},
|
||
update(deco, tr) {
|
||
if (!tr.docChanged && !tr.selection)
|
||
return deco;
|
||
let decorations = [];
|
||
let config = tr.state.facet(bracketMatchingConfig);
|
||
for (let range of tr.state.selection.ranges) {
|
||
if (!range.empty)
|
||
continue;
|
||
let match = matchBrackets(tr.state, range.head, -1, config) || range.head > 0 && matchBrackets(tr.state, range.head - 1, 1, config) || config.afterCursor && (matchBrackets(tr.state, range.head, 1, config) || range.head < tr.state.doc.length && matchBrackets(tr.state, range.head + 1, -1, config));
|
||
if (match)
|
||
decorations = decorations.concat(config.renderMatch(match, tr.state));
|
||
}
|
||
return Decoration.set(decorations, true);
|
||
},
|
||
provide: (f) => EditorView.decorations.from(f)
|
||
});
|
||
var bracketMatchingUnique = [
|
||
bracketMatchingState,
|
||
baseTheme
|
||
];
|
||
function bracketMatching(config = {}) {
|
||
return [bracketMatchingConfig.of(config), bracketMatchingUnique];
|
||
}
|
||
var bracketMatchingHandle = new NodeProp();
|
||
function matchingNodes(node, dir, brackets) {
|
||
let byProp = node.prop(dir < 0 ? NodeProp.openedBy : NodeProp.closedBy);
|
||
if (byProp)
|
||
return byProp;
|
||
if (node.name.length == 1) {
|
||
let index = brackets.indexOf(node.name);
|
||
if (index > -1 && index % 2 == (dir < 0 ? 1 : 0))
|
||
return [brackets[index + dir]];
|
||
}
|
||
return null;
|
||
}
|
||
function findHandle(node) {
|
||
let hasHandle = node.type.prop(bracketMatchingHandle);
|
||
return hasHandle ? hasHandle(node.node) : node;
|
||
}
|
||
function matchBrackets(state, pos, dir, config = {}) {
|
||
let maxScanDistance = config.maxScanDistance || DefaultScanDist, brackets = config.brackets || DefaultBrackets;
|
||
let tree = syntaxTree(state), node = tree.resolveInner(pos, dir);
|
||
for (let cur = node; cur; cur = cur.parent) {
|
||
let matches = matchingNodes(cur.type, dir, brackets);
|
||
if (matches && cur.from < cur.to) {
|
||
let handle = findHandle(cur);
|
||
if (handle && (dir > 0 ? pos >= handle.from && pos < handle.to : pos > handle.from && pos <= handle.to))
|
||
return matchMarkedBrackets(state, pos, dir, cur, handle, matches, brackets);
|
||
}
|
||
}
|
||
return matchPlainBrackets(state, pos, dir, tree, node.type, maxScanDistance, brackets);
|
||
}
|
||
function matchMarkedBrackets(_state, _pos, dir, token, handle, matching, brackets) {
|
||
let parent = token.parent, firstToken = { from: handle.from, to: handle.to };
|
||
let depth = 0, cursor = parent === null || parent === void 0 ? void 0 : parent.cursor();
|
||
if (cursor && (dir < 0 ? cursor.childBefore(token.from) : cursor.childAfter(token.to)))
|
||
do {
|
||
if (dir < 0 ? cursor.to <= token.from : cursor.from >= token.to) {
|
||
if (depth == 0 && matching.indexOf(cursor.type.name) > -1 && cursor.from < cursor.to) {
|
||
let endHandle = findHandle(cursor);
|
||
return { start: firstToken, end: endHandle ? { from: endHandle.from, to: endHandle.to } : void 0, matched: true };
|
||
} else if (matchingNodes(cursor.type, dir, brackets)) {
|
||
depth++;
|
||
} else if (matchingNodes(cursor.type, -dir, brackets)) {
|
||
if (depth == 0) {
|
||
let endHandle = findHandle(cursor);
|
||
return {
|
||
start: firstToken,
|
||
end: endHandle && endHandle.from < endHandle.to ? { from: endHandle.from, to: endHandle.to } : void 0,
|
||
matched: false
|
||
};
|
||
}
|
||
depth--;
|
||
}
|
||
}
|
||
} while (dir < 0 ? cursor.prevSibling() : cursor.nextSibling());
|
||
return { start: firstToken, matched: false };
|
||
}
|
||
function matchPlainBrackets(state, pos, dir, tree, tokenType, maxScanDistance, brackets) {
|
||
let startCh = dir < 0 ? state.sliceDoc(pos - 1, pos) : state.sliceDoc(pos, pos + 1);
|
||
let bracket = brackets.indexOf(startCh);
|
||
if (bracket < 0 || bracket % 2 == 0 != dir > 0)
|
||
return null;
|
||
let startToken = { from: dir < 0 ? pos - 1 : pos, to: dir > 0 ? pos + 1 : pos };
|
||
let iter = state.doc.iterRange(pos, dir > 0 ? state.doc.length : 0), depth = 0;
|
||
for (let distance = 0; !iter.next().done && distance <= maxScanDistance; ) {
|
||
let text = iter.value;
|
||
if (dir < 0)
|
||
distance += text.length;
|
||
let basePos = pos + distance * dir;
|
||
for (let pos2 = dir > 0 ? 0 : text.length - 1, end = dir > 0 ? text.length : -1; pos2 != end; pos2 += dir) {
|
||
let found = brackets.indexOf(text[pos2]);
|
||
if (found < 0 || tree.resolveInner(basePos + pos2, 1).type != tokenType)
|
||
continue;
|
||
if (found % 2 == 0 == dir > 0) {
|
||
depth++;
|
||
} else if (depth == 1) {
|
||
return { start: startToken, end: { from: basePos + pos2, to: basePos + pos2 + 1 }, matched: found >> 1 == bracket >> 1 };
|
||
} else {
|
||
depth--;
|
||
}
|
||
}
|
||
if (dir > 0)
|
||
distance += text.length;
|
||
}
|
||
return iter.done ? { start: startToken, matched: false } : null;
|
||
}
|
||
function countCol(string, end, tabSize, startIndex = 0, startValue = 0) {
|
||
if (end == null) {
|
||
end = string.search(/[^\s\u00a0]/);
|
||
if (end == -1)
|
||
end = string.length;
|
||
}
|
||
let n = startValue;
|
||
for (let i = startIndex; i < end; i++) {
|
||
if (string.charCodeAt(i) == 9)
|
||
n += tabSize - n % tabSize;
|
||
else
|
||
n++;
|
||
}
|
||
return n;
|
||
}
|
||
var StringStream = class {
|
||
/**
|
||
Create a stream.
|
||
*/
|
||
constructor(string, tabSize, indentUnit2, overrideIndent) {
|
||
this.string = string;
|
||
this.tabSize = tabSize;
|
||
this.indentUnit = indentUnit2;
|
||
this.overrideIndent = overrideIndent;
|
||
this.pos = 0;
|
||
this.start = 0;
|
||
this.lastColumnPos = 0;
|
||
this.lastColumnValue = 0;
|
||
}
|
||
/**
|
||
True if we are at the end of the line.
|
||
*/
|
||
eol() {
|
||
return this.pos >= this.string.length;
|
||
}
|
||
/**
|
||
True if we are at the start of the line.
|
||
*/
|
||
sol() {
|
||
return this.pos == 0;
|
||
}
|
||
/**
|
||
Get the next code unit after the current position, or undefined
|
||
if we're at the end of the line.
|
||
*/
|
||
peek() {
|
||
return this.string.charAt(this.pos) || void 0;
|
||
}
|
||
/**
|
||
Read the next code unit and advance `this.pos`.
|
||
*/
|
||
next() {
|
||
if (this.pos < this.string.length)
|
||
return this.string.charAt(this.pos++);
|
||
}
|
||
/**
|
||
Match the next character against the given string, regular
|
||
expression, or predicate. Consume and return it if it matches.
|
||
*/
|
||
eat(match) {
|
||
let ch = this.string.charAt(this.pos);
|
||
let ok;
|
||
if (typeof match == "string")
|
||
ok = ch == match;
|
||
else
|
||
ok = ch && (match instanceof RegExp ? match.test(ch) : match(ch));
|
||
if (ok) {
|
||
++this.pos;
|
||
return ch;
|
||
}
|
||
}
|
||
/**
|
||
Continue matching characters that match the given string,
|
||
regular expression, or predicate function. Return true if any
|
||
characters were consumed.
|
||
*/
|
||
eatWhile(match) {
|
||
let start = this.pos;
|
||
while (this.eat(match)) {
|
||
}
|
||
return this.pos > start;
|
||
}
|
||
/**
|
||
Consume whitespace ahead of `this.pos`. Return true if any was
|
||
found.
|
||
*/
|
||
eatSpace() {
|
||
let start = this.pos;
|
||
while (/[\s\u00a0]/.test(this.string.charAt(this.pos)))
|
||
++this.pos;
|
||
return this.pos > start;
|
||
}
|
||
/**
|
||
Move to the end of the line.
|
||
*/
|
||
skipToEnd() {
|
||
this.pos = this.string.length;
|
||
}
|
||
/**
|
||
Move to directly before the given character, if found on the
|
||
current line.
|
||
*/
|
||
skipTo(ch) {
|
||
let found = this.string.indexOf(ch, this.pos);
|
||
if (found > -1) {
|
||
this.pos = found;
|
||
return true;
|
||
}
|
||
}
|
||
/**
|
||
Move back `n` characters.
|
||
*/
|
||
backUp(n) {
|
||
this.pos -= n;
|
||
}
|
||
/**
|
||
Get the column position at `this.pos`.
|
||
*/
|
||
column() {
|
||
if (this.lastColumnPos < this.start) {
|
||
this.lastColumnValue = countCol(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
|
||
this.lastColumnPos = this.start;
|
||
}
|
||
return this.lastColumnValue;
|
||
}
|
||
/**
|
||
Get the indentation column of the current line.
|
||
*/
|
||
indentation() {
|
||
var _a2;
|
||
return (_a2 = this.overrideIndent) !== null && _a2 !== void 0 ? _a2 : countCol(this.string, null, this.tabSize);
|
||
}
|
||
/**
|
||
Match the input against the given string or regular expression
|
||
(which should start with a `^`). Return true or the regexp match
|
||
if it matches.
|
||
|
||
Unless `consume` is set to `false`, this will move `this.pos`
|
||
past the matched text.
|
||
|
||
When matching a string `caseInsensitive` can be set to true to
|
||
make the match case-insensitive.
|
||
*/
|
||
match(pattern, consume, caseInsensitive) {
|
||
if (typeof pattern == "string") {
|
||
let cased = (str) => caseInsensitive ? str.toLowerCase() : str;
|
||
let substr = this.string.substr(this.pos, pattern.length);
|
||
if (cased(substr) == cased(pattern)) {
|
||
if (consume !== false)
|
||
this.pos += pattern.length;
|
||
return true;
|
||
} else
|
||
return null;
|
||
} else {
|
||
let match = this.string.slice(this.pos).match(pattern);
|
||
if (match && match.index > 0)
|
||
return null;
|
||
if (match && consume !== false)
|
||
this.pos += match[0].length;
|
||
return match;
|
||
}
|
||
}
|
||
/**
|
||
Get the current token.
|
||
*/
|
||
current() {
|
||
return this.string.slice(this.start, this.pos);
|
||
}
|
||
};
|
||
function fullParser(spec) {
|
||
return {
|
||
name: spec.name || "",
|
||
token: spec.token,
|
||
blankLine: spec.blankLine || (() => {
|
||
}),
|
||
startState: spec.startState || (() => true),
|
||
copyState: spec.copyState || defaultCopyState,
|
||
indent: spec.indent || (() => null),
|
||
languageData: spec.languageData || {},
|
||
tokenTable: spec.tokenTable || noTokens
|
||
};
|
||
}
|
||
function defaultCopyState(state) {
|
||
if (typeof state != "object")
|
||
return state;
|
||
let newState = {};
|
||
for (let prop in state) {
|
||
let val = state[prop];
|
||
newState[prop] = val instanceof Array ? val.slice() : val;
|
||
}
|
||
return newState;
|
||
}
|
||
var IndentedFrom = /* @__PURE__ */ new WeakMap();
|
||
var StreamLanguage = class _StreamLanguage extends Language {
|
||
constructor(parser) {
|
||
let data = defineLanguageFacet(parser.languageData);
|
||
let p = fullParser(parser), self;
|
||
let impl = new class extends Parser {
|
||
createParse(input, fragments, ranges) {
|
||
return new Parse(self, input, fragments, ranges);
|
||
}
|
||
}();
|
||
super(data, impl, [indentService.of((cx, pos) => this.getIndent(cx, pos))], parser.name);
|
||
this.topNode = docID(data);
|
||
self = this;
|
||
this.streamParser = p;
|
||
this.stateAfter = new NodeProp({ perNode: true });
|
||
this.tokenTable = parser.tokenTable ? new TokenTable(p.tokenTable) : defaultTokenTable;
|
||
}
|
||
/**
|
||
Define a stream language.
|
||
*/
|
||
static define(spec) {
|
||
return new _StreamLanguage(spec);
|
||
}
|
||
getIndent(cx, pos) {
|
||
let tree = syntaxTree(cx.state), at = tree.resolve(pos);
|
||
while (at && at.type != this.topNode)
|
||
at = at.parent;
|
||
if (!at)
|
||
return null;
|
||
let from = void 0;
|
||
let { overrideIndentation } = cx.options;
|
||
if (overrideIndentation) {
|
||
from = IndentedFrom.get(cx.state);
|
||
if (from != null && from < pos - 1e4)
|
||
from = void 0;
|
||
}
|
||
let start = findState(this, tree, 0, at.from, from !== null && from !== void 0 ? from : pos), statePos, state;
|
||
if (start) {
|
||
state = start.state;
|
||
statePos = start.pos + 1;
|
||
} else {
|
||
state = this.streamParser.startState(cx.unit);
|
||
statePos = 0;
|
||
}
|
||
if (pos - statePos > 1e4)
|
||
return null;
|
||
while (statePos < pos) {
|
||
let line2 = cx.state.doc.lineAt(statePos), end = Math.min(pos, line2.to);
|
||
if (line2.length) {
|
||
let indentation = overrideIndentation ? overrideIndentation(line2.from) : -1;
|
||
let stream = new StringStream(line2.text, cx.state.tabSize, cx.unit, indentation < 0 ? void 0 : indentation);
|
||
while (stream.pos < end - line2.from)
|
||
readToken(this.streamParser.token, stream, state);
|
||
} else {
|
||
this.streamParser.blankLine(state, cx.unit);
|
||
}
|
||
if (end == pos)
|
||
break;
|
||
statePos = line2.to + 1;
|
||
}
|
||
let line = cx.lineAt(pos);
|
||
if (overrideIndentation && from == null)
|
||
IndentedFrom.set(cx.state, line.from);
|
||
return this.streamParser.indent(state, /^\s*(.*)/.exec(line.text)[1], cx);
|
||
}
|
||
get allowsNesting() {
|
||
return false;
|
||
}
|
||
};
|
||
function findState(lang, tree, off, startPos, before) {
|
||
let state = off >= startPos && off + tree.length <= before && tree.prop(lang.stateAfter);
|
||
if (state)
|
||
return { state: lang.streamParser.copyState(state), pos: off + tree.length };
|
||
for (let i = tree.children.length - 1; i >= 0; i--) {
|
||
let child = tree.children[i], pos = off + tree.positions[i];
|
||
let found = child instanceof Tree && pos < before && findState(lang, child, pos, startPos, before);
|
||
if (found)
|
||
return found;
|
||
}
|
||
return null;
|
||
}
|
||
function cutTree(lang, tree, from, to, inside) {
|
||
if (inside && from <= 0 && to >= tree.length)
|
||
return tree;
|
||
if (!inside && tree.type == lang.topNode)
|
||
inside = true;
|
||
for (let i = tree.children.length - 1; i >= 0; i--) {
|
||
let pos = tree.positions[i], child = tree.children[i], inner;
|
||
if (pos < to && child instanceof Tree) {
|
||
if (!(inner = cutTree(lang, child, from - pos, to - pos, inside)))
|
||
break;
|
||
return !inside ? inner : new Tree(tree.type, tree.children.slice(0, i).concat(inner), tree.positions.slice(0, i + 1), pos + inner.length);
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function findStartInFragments(lang, fragments, startPos, editorState) {
|
||
for (let f of fragments) {
|
||
let from = f.from + (f.openStart ? 25 : 0), to = f.to - (f.openEnd ? 25 : 0);
|
||
let found = from <= startPos && to > startPos && findState(lang, f.tree, 0 - f.offset, startPos, to), tree;
|
||
if (found && (tree = cutTree(lang, f.tree, startPos + f.offset, found.pos + f.offset, false)))
|
||
return { state: found.state, tree };
|
||
}
|
||
return { state: lang.streamParser.startState(editorState ? getIndentUnit(editorState) : 4), tree: Tree.empty };
|
||
}
|
||
var Parse = class {
|
||
constructor(lang, input, fragments, ranges) {
|
||
this.lang = lang;
|
||
this.input = input;
|
||
this.fragments = fragments;
|
||
this.ranges = ranges;
|
||
this.stoppedAt = null;
|
||
this.chunks = [];
|
||
this.chunkPos = [];
|
||
this.chunk = [];
|
||
this.chunkReused = void 0;
|
||
this.rangeIndex = 0;
|
||
this.to = ranges[ranges.length - 1].to;
|
||
let context = ParseContext.get(), from = ranges[0].from;
|
||
let { state, tree } = findStartInFragments(lang, fragments, from, context === null || context === void 0 ? void 0 : context.state);
|
||
this.state = state;
|
||
this.parsedPos = this.chunkStart = from + tree.length;
|
||
for (let i = 0; i < tree.children.length; i++) {
|
||
this.chunks.push(tree.children[i]);
|
||
this.chunkPos.push(tree.positions[i]);
|
||
}
|
||
if (context && this.parsedPos < context.viewport.from - 1e5) {
|
||
this.state = this.lang.streamParser.startState(getIndentUnit(context.state));
|
||
context.skipUntilInView(this.parsedPos, context.viewport.from);
|
||
this.parsedPos = context.viewport.from;
|
||
}
|
||
this.moveRangeIndex();
|
||
}
|
||
advance() {
|
||
let context = ParseContext.get();
|
||
let parseEnd = this.stoppedAt == null ? this.to : Math.min(this.to, this.stoppedAt);
|
||
let end = Math.min(
|
||
parseEnd,
|
||
this.chunkStart + 2048
|
||
/* ChunkSize */
|
||
);
|
||
if (context)
|
||
end = Math.min(end, context.viewport.to);
|
||
while (this.parsedPos < end)
|
||
this.parseLine(context);
|
||
if (this.chunkStart < this.parsedPos)
|
||
this.finishChunk();
|
||
if (this.parsedPos >= parseEnd)
|
||
return this.finish();
|
||
if (context && this.parsedPos >= context.viewport.to) {
|
||
context.skipUntilInView(this.parsedPos, parseEnd);
|
||
return this.finish();
|
||
}
|
||
return null;
|
||
}
|
||
stopAt(pos) {
|
||
this.stoppedAt = pos;
|
||
}
|
||
lineAfter(pos) {
|
||
let chunk = this.input.chunk(pos);
|
||
if (!this.input.lineChunks) {
|
||
let eol = chunk.indexOf("\n");
|
||
if (eol > -1)
|
||
chunk = chunk.slice(0, eol);
|
||
} else if (chunk == "\n") {
|
||
chunk = "";
|
||
}
|
||
return pos + chunk.length <= this.to ? chunk : chunk.slice(0, this.to - pos);
|
||
}
|
||
nextLine() {
|
||
let from = this.parsedPos, line = this.lineAfter(from), end = from + line.length;
|
||
for (let index = this.rangeIndex; ; ) {
|
||
let rangeEnd = this.ranges[index].to;
|
||
if (rangeEnd >= end)
|
||
break;
|
||
line = line.slice(0, rangeEnd - (end - line.length));
|
||
index++;
|
||
if (index == this.ranges.length)
|
||
break;
|
||
let rangeStart = this.ranges[index].from;
|
||
let after = this.lineAfter(rangeStart);
|
||
line += after;
|
||
end = rangeStart + after.length;
|
||
}
|
||
return { line, end };
|
||
}
|
||
skipGapsTo(pos, offset, side) {
|
||
for (; ; ) {
|
||
let end = this.ranges[this.rangeIndex].to, offPos = pos + offset;
|
||
if (side > 0 ? end > offPos : end >= offPos)
|
||
break;
|
||
let start = this.ranges[++this.rangeIndex].from;
|
||
offset += start - end;
|
||
}
|
||
return offset;
|
||
}
|
||
moveRangeIndex() {
|
||
while (this.ranges[this.rangeIndex].to < this.parsedPos)
|
||
this.rangeIndex++;
|
||
}
|
||
emitToken(id, from, to, size, offset) {
|
||
if (this.ranges.length > 1) {
|
||
offset = this.skipGapsTo(from, offset, 1);
|
||
from += offset;
|
||
let len0 = this.chunk.length;
|
||
offset = this.skipGapsTo(to, offset, -1);
|
||
to += offset;
|
||
size += this.chunk.length - len0;
|
||
}
|
||
this.chunk.push(id, from, to, size);
|
||
return offset;
|
||
}
|
||
parseLine(context) {
|
||
let { line, end } = this.nextLine(), offset = 0, { streamParser } = this.lang;
|
||
let stream = new StringStream(line, context ? context.state.tabSize : 4, context ? getIndentUnit(context.state) : 2);
|
||
if (stream.eol()) {
|
||
streamParser.blankLine(this.state, stream.indentUnit);
|
||
} else {
|
||
while (!stream.eol()) {
|
||
let token = readToken(streamParser.token, stream, this.state);
|
||
if (token)
|
||
offset = this.emitToken(this.lang.tokenTable.resolve(token), this.parsedPos + stream.start, this.parsedPos + stream.pos, 4, offset);
|
||
if (stream.start > 1e4)
|
||
break;
|
||
}
|
||
}
|
||
this.parsedPos = end;
|
||
this.moveRangeIndex();
|
||
if (this.parsedPos < this.to)
|
||
this.parsedPos++;
|
||
}
|
||
finishChunk() {
|
||
let tree = Tree.build({
|
||
buffer: this.chunk,
|
||
start: this.chunkStart,
|
||
length: this.parsedPos - this.chunkStart,
|
||
nodeSet,
|
||
topID: 0,
|
||
maxBufferLength: 2048,
|
||
reused: this.chunkReused
|
||
});
|
||
tree = new Tree(tree.type, tree.children, tree.positions, tree.length, [[this.lang.stateAfter, this.lang.streamParser.copyState(this.state)]]);
|
||
this.chunks.push(tree);
|
||
this.chunkPos.push(this.chunkStart - this.ranges[0].from);
|
||
this.chunk = [];
|
||
this.chunkReused = void 0;
|
||
this.chunkStart = this.parsedPos;
|
||
}
|
||
finish() {
|
||
return new Tree(this.lang.topNode, this.chunks, this.chunkPos, this.parsedPos - this.ranges[0].from).balance();
|
||
}
|
||
};
|
||
function readToken(token, stream, state) {
|
||
stream.start = stream.pos;
|
||
for (let i = 0; i < 10; i++) {
|
||
let result = token(stream, state);
|
||
if (stream.pos > stream.start)
|
||
return result;
|
||
}
|
||
throw new Error("Stream parser failed to advance stream.");
|
||
}
|
||
var noTokens = /* @__PURE__ */ Object.create(null);
|
||
var typeArray = [NodeType.none];
|
||
var nodeSet = new NodeSet(typeArray);
|
||
var warned = [];
|
||
var defaultTable = /* @__PURE__ */ Object.create(null);
|
||
for (let [legacyName, name] of [
|
||
["variable", "variableName"],
|
||
["variable-2", "variableName.special"],
|
||
["string-2", "string.special"],
|
||
["def", "variableName.definition"],
|
||
["tag", "tagName"],
|
||
["attribute", "attributeName"],
|
||
["type", "typeName"],
|
||
["builtin", "variableName.standard"],
|
||
["qualifier", "modifier"],
|
||
["error", "invalid"],
|
||
["header", "heading"],
|
||
["property", "propertyName"]
|
||
])
|
||
defaultTable[legacyName] = createTokenType(noTokens, name);
|
||
var TokenTable = class {
|
||
constructor(extra) {
|
||
this.extra = extra;
|
||
this.table = Object.assign(/* @__PURE__ */ Object.create(null), defaultTable);
|
||
}
|
||
resolve(tag) {
|
||
return !tag ? 0 : this.table[tag] || (this.table[tag] = createTokenType(this.extra, tag));
|
||
}
|
||
};
|
||
var defaultTokenTable = new TokenTable(noTokens);
|
||
function warnForPart(part, msg) {
|
||
if (warned.indexOf(part) > -1)
|
||
return;
|
||
warned.push(part);
|
||
console.warn(msg);
|
||
}
|
||
function createTokenType(extra, tagStr) {
|
||
let tag = null;
|
||
for (let part of tagStr.split(".")) {
|
||
let value = extra[part] || tags[part];
|
||
if (!value) {
|
||
warnForPart(part, `Unknown highlighting tag ${part}`);
|
||
} else if (typeof value == "function") {
|
||
if (!tag)
|
||
warnForPart(part, `Modifier ${part} used at start of tag`);
|
||
else
|
||
tag = value(tag);
|
||
} else {
|
||
if (tag)
|
||
warnForPart(part, `Tag ${part} used as modifier`);
|
||
else
|
||
tag = value;
|
||
}
|
||
}
|
||
if (!tag)
|
||
return 0;
|
||
let name = tagStr.replace(/ /g, "_"), type = NodeType.define({
|
||
id: typeArray.length,
|
||
name,
|
||
props: [styleTags({ [name]: tag })]
|
||
});
|
||
typeArray.push(type);
|
||
return type.id;
|
||
}
|
||
function docID(data) {
|
||
let type = NodeType.define({ id: typeArray.length, name: "Document", props: [languageDataProp.add(() => data)] });
|
||
typeArray.push(type);
|
||
return type;
|
||
}
|
||
|
||
export {
|
||
languageDataProp,
|
||
defineLanguageFacet,
|
||
sublanguageProp,
|
||
Language,
|
||
LRLanguage,
|
||
syntaxTree,
|
||
ensureSyntaxTree,
|
||
syntaxTreeAvailable,
|
||
forceParsing,
|
||
syntaxParserRunning,
|
||
DocInput,
|
||
ParseContext,
|
||
language,
|
||
LanguageSupport,
|
||
LanguageDescription,
|
||
indentService,
|
||
indentUnit,
|
||
getIndentUnit,
|
||
indentString,
|
||
getIndentation,
|
||
indentRange,
|
||
IndentContext,
|
||
indentNodeProp,
|
||
TreeIndentContext,
|
||
delimitedIndent,
|
||
flatIndent,
|
||
continuedIndent,
|
||
indentOnInput,
|
||
foldService,
|
||
foldNodeProp,
|
||
foldInside,
|
||
foldable,
|
||
foldEffect,
|
||
unfoldEffect,
|
||
foldState,
|
||
foldedRanges,
|
||
foldCode,
|
||
unfoldCode,
|
||
foldAll,
|
||
unfoldAll,
|
||
toggleFold,
|
||
foldKeymap,
|
||
codeFolding,
|
||
foldGutter,
|
||
HighlightStyle,
|
||
syntaxHighlighting,
|
||
highlightingFor,
|
||
defaultHighlightStyle,
|
||
bracketMatching,
|
||
bracketMatchingHandle,
|
||
matchBrackets,
|
||
StringStream,
|
||
StreamLanguage
|
||
};
|
||
//# sourceMappingURL=chunk-D63OSQ34.js.map
|