cm-tarnation
Version:
An alternative parser for CodeMirror 6
109 lines • 4.26 kB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
import { Wrapping } from "../../constants";
import { createID } from "../../util";
import { Node } from "../node";
import { Rule } from "./rule";
/**
* A sort of {@link Rule}-like object that affects a {@link GrammarStack}. It
* has `begin` and `end` properties which are {@link Rule}s that are used
* for switching states.
*/
export class State {
/**
* @param repo - The {@link Repository} to add this state to.
* @param state - The definition for this state.
*/
constructor(repo, state) {
let type = state.type ?? createID();
let emit = (state.type && state.emit !== false) || state.autocomplete;
// states handle nesting differently, so we don't want
// to use the normal nesting behavior
if (state.nest) {
const nest = state.nest;
delete state.nest; // don't pass to node
this.inside = new Node(repo.id(), { type: `${type}_Nest${nest}`, nest });
}
this.name = type;
this.node = !emit ? Node.None : new Node(repo.id(), state);
// prettier-ignore
const begin = typeof state.begin === "string"
? repo.get(state.begin)
: repo.add(state.begin);
// prettier-ignore
const end = typeof state.end === "string"
? repo.get(state.end)
: repo.add(state.end);
if (!(begin instanceof Rule) || !(end instanceof Rule)) {
throw new Error(`Invalid state ${type}, rules not found or weren't rules`);
}
this.begin = begin;
this.end = end;
if (!this.inside) {
if (state.inside) {
if (state.inside === "loose")
this.loose = true;
else if (state.inside === "inherit")
this.inside = null;
else if (!Array.isArray(state.inside))
this.inside = repo.add(state.inside);
else
this.inside = repo.inside(state.inside);
}
else {
this.inside = null;
}
}
}
/**
* @param state - The current {@link GrammarState}.
* @param str - The string to match.
* @param pos - The position to start matching at.
*/
match(state, str, pos) {
// loose mode, doesn't actually affect the stack
if (this.loose) {
const endMatched = this.end.match(state, str, pos);
if (endMatched)
return endMatched.wrap(this.node, Wrapping.END);
const beginMatched = this.begin.match(state, str, pos);
if (beginMatched)
return beginMatched.wrap(this.node, Wrapping.BEGIN);
return null;
}
else {
let matched = this.begin.match(state, str, pos);
if (!matched)
return null;
matched = matched.wrap(this.node, Wrapping.BEGIN);
if (this.inside instanceof Node) {
matched.push(this.inside, 1);
state.stack.push(this.node, [], this);
}
else {
const inside = this.inside ? this.inside : state.stack.rules;
state.stack.push(this.node, inside, this);
}
return matched;
}
}
/**
* @param state - The current {@link GrammarState}.
* @param str - The string to match.
* @param pos - The position to start matching at.
*/
close(state, str, pos) {
if (this.loose)
throw new Error("Closing a loose state is not supported");
let matched = this.end.match(state, str, pos);
if (!matched)
return null;
matched = matched.wrap(this.node, Wrapping.END);
if (this.inside instanceof Node)
matched.push(this.inside, -1);
matched.state.stack.pop();
return matched;
}
}
//# sourceMappingURL=state.js.map