// FIXME profile adding a per-Tree TreeNode cache, validating it by // parent pointer /// The default maximum length of a `TreeBuffer` node. const DefaultBufferLength = 1024; let nextPropID = 0; class Range { constructor(from, to) { this.from = from; this.to = to; } } /// Each [node type](#common.NodeType) or [individual tree](#common.Tree) /// can have metadata associated with it in props. Instances of this /// class represent prop names. class NodeProp { /// Create a new node prop type. constructor(config = {}) { this.id = nextPropID++; this.perNode = !!config.perNode; this.deserialize = config.deserialize || (() => { throw new Error("This node type doesn't define a deserialize function"); }); } /// This is meant to be used with /// [`NodeSet.extend`](#common.NodeSet.extend) or /// [`LRParser.configure`](#lr.ParserConfig.props) to compute /// prop values for each node type in the set. Takes a [match /// object](#common.NodeType^match) or function that returns undefined /// if the node type doesn't get this prop, and the prop's value if /// it does. add(match) { if (this.perNode) throw new RangeError("Can't add per-node props to node types"); if (typeof match != "function") match = NodeType.match(match); return (type) => { let result = match(type); return result === undefined ? null : [this, result]; }; } } /// Prop that is used to describe matching delimiters. For opening /// delimiters, this holds an array of node names (written as a /// space-separated string when declaring this prop in a grammar) /// for the node types of closing delimiters that match it. NodeProp.closedBy = new NodeProp({ deserialize: str => str.split(" ") }); /// The inverse of [`closedBy`](#common.NodeProp^closedBy). This is /// attached to closing delimiters, holding an array of node names /// of types of matching opening delimiters. NodeProp.openedBy = new NodeProp({ deserialize: str => str.split(" ") }); /// Used to assign node types to groups (for example, all node /// types that represent an expression could be tagged with an /// `"Expression"` group). NodeProp.group = new NodeProp({ deserialize: str => str.split(" ") }); /// The hash of the [context](#lr.ContextTracker.constructor) /// that the node was parsed in, if any. Used to limit reuse of /// contextual nodes. NodeProp.contextHash = new NodeProp({ perNode: true }); /// The distance beyond the end of the node that the tokenizer /// looked ahead for any of the tokens inside the node. (The LR /// parser only stores this when it is larger than 25, for /// efficiency reasons.) NodeProp.lookAhead = new NodeProp({ perNode: true }); /// This per-node prop is used to replace a given node, or part of a /// node, with another tree. This is useful to include trees from /// different languages in mixed-language parsers. NodeProp.mounted = new NodeProp({ perNode: true }); /// A mounted tree, which can be [stored](#common.NodeProp^mounted) on /// a tree node to indicate that parts of its content are /// represented by another tree. class MountedTree { constructor( /// The inner tree. tree, /// If this is null, this tree replaces the entire node (it will /// be included in the regular iteration instead of its host /// node). If not, only the given ranges are considered to be /// covered by this tree. This is used for trees that are mixed in /// a way that isn't strictly hierarchical. Such mounted trees are /// only entered by [`resolveInner`](#common.Tree.resolveInner) /// and [`enter`](#common.SyntaxNode.enter). overlay, /// The parser used to create this subtree. parser) { this.tree = tree; this.overlay = overlay; this.parser = parser; } } const noProps = Object.create(null); /// Each node in a syntax tree has a node type associated with it. class NodeType { /// @internal constructor( /// The name of the node type. Not necessarily unique, but if the /// grammar was written properly, different node types with the /// same name within a node set should play the same semantic /// role. name, /// @internal props, /// The id of this node in its set. Corresponds to the term ids /// used in the parser. id, /// @internal flags = 0) { this.name = name; this.props = props; this.id = id; this.flags = flags; } /// Define a node type. static define(spec) { let props = spec.props && spec.props.length ? Object.create(null) : noProps; let flags = (spec.top ? 1 /* Top */ : 0) | (spec.skipped ? 2 /* Skipped */ : 0) | (spec.error ? 4 /* Error */ : 0) | (spec.name == null ? 8 /* Anonymous */ : 0); let type = new NodeType(spec.name || "", props, spec.id, flags); if (spec.props) for (let src of spec.props) { if (!Array.isArray(src)) src = src(type); if (src) { if (src[0].perNode) throw new RangeError("Can't store a per-node prop on a node type"); props[src[0].id] = src[1]; } } return type; } /// Retrieves a node prop for this type. Will return `undefined` if /// the prop isn't present on this node. prop(prop) { return this.props[prop.id]; } /// True when this is the top node of a grammar. get isTop() { return (this.flags & 1 /* Top */) > 0; } /// True when this node is produced by a skip rule. get isSkipped() { return (this.flags & 2 /* Skipped */) > 0; } /// Indicates whether this is an error node. get isError() { return (this.flags & 4 /* Error */) > 0; } /// When true, this node type doesn't correspond to a user-declared /// named node, for example because it is used to cache repetition. get isAnonymous() { return (this.flags & 8 /* Anonymous */) > 0; } /// Returns true when this node's name or one of its /// [groups](#common.NodeProp^group) matches the given string. is(name) { if (typeof name == 'string') { if (this.name == name) return true; let group = this.prop(NodeProp.group); return group ? group.indexOf(name) > -1 : false; } return this.id == name; } /// Create a function from node types to arbitrary values by /// specifying an object whose property names are node or /// [group](#common.NodeProp^group) names. Often useful with /// [`NodeProp.add`](#common.NodeProp.add). You can put multiple /// names, separated by spaces, in a single property name to map /// multiple node names to a single value. static match(map) { let direct = Object.create(null); for (let prop in map) for (let name of prop.split(" ")) direct[name] = map[prop]; return (node) => { for (let groups = node.prop(NodeProp.group), i = -1; i < (groups ? groups.length : 0); i++) { let found = direct[i < 0 ? node.name : groups[i]]; if (found) return found; } }; } } /// An empty dummy node type to use when no actual type is available. NodeType.none = new NodeType("", Object.create(null), 0, 8 /* Anonymous */); /// A node set holds a collection of node types. It is used to /// compactly represent trees by storing their type ids, rather than a /// full pointer to the type object, in a numeric array. Each parser /// [has](#lr.LRParser.nodeSet) a node set, and [tree /// buffers](#common.TreeBuffer) can only store collections of nodes /// from the same set. A set can have a maximum of 2**16 (65536) node /// types in it, so that the ids fit into 16-bit typed array slots. class NodeSet { /// Create a set with the given types. The `id` property of each /// type should correspond to its position within the array. constructor( /// The node types in this set, by id. types) { this.types = types; for (let i = 0; i < types.length; i++) if (types[i].id != i) throw new RangeError("Node type ids should correspond to array positions when creating a node set"); } /// Create a copy of this set with some node properties added. The /// arguments to this method can be created with /// [`NodeProp.add`](#common.NodeProp.add). extend(...props) { let newTypes = []; for (let type of this.types) { let newProps = null; for (let source of props) { let add = source(type); if (add) { if (!newProps) newProps = Object.assign({}, type.props); newProps[add[0].id] = add[1]; } } newTypes.push(newProps ? new NodeType(type.name, newProps, type.id, type.flags) : type); } return new NodeSet(newTypes); } } const CachedNode = new WeakMap(), CachedInnerNode = new WeakMap(); /// Options that control iteration. Can be combined with the `|` /// operator to enable multiple ones. var IterMode; (function (IterMode) { /// When enabled, iteration will only visit [`Tree`](#common.Tree) /// objects, not nodes packed into /// [`TreeBuffer`](#common.TreeBuffer)s. IterMode[IterMode["ExcludeBuffers"] = 1] = "ExcludeBuffers"; /// Enable this to make iteration include anonymous nodes (such as /// the nodes that wrap repeated grammar constructs into a balanced /// tree). IterMode[IterMode["IncludeAnonymous"] = 2] = "IncludeAnonymous"; /// By default, regular [mounted](#common.NodeProp^mounted) nodes /// replace their base node in iteration. Enable this to ignore them /// instead. IterMode[IterMode["IgnoreMounts"] = 4] = "IgnoreMounts"; /// This option only applies in /// [`enter`](#common.SyntaxNode.enter)-style methods. It tells the /// library to not enter mounted overlays if one covers the given /// position. IterMode[IterMode["IgnoreOverlays"] = 8] = "IgnoreOverlays"; })(IterMode || (IterMode = {})); /// A piece of syntax tree. There are two ways to approach these /// trees: the way they are actually stored in memory, and the /// convenient way. /// /// Syntax trees are stored as a tree of `Tree` and `TreeBuffer` /// objects. By packing detail information into `TreeBuffer` leaf /// nodes, the representation is made a lot more memory-efficient. /// /// However, when you want to actually work with tree nodes, this /// representation is very awkward, so most client code will want to /// use the [`TreeCursor`](#common.TreeCursor) or /// [`SyntaxNode`](#common.SyntaxNode) interface instead, which provides /// a view on some part of this data structure, and can be used to /// move around to adjacent nodes. class Tree { /// Construct a new tree. See also [`Tree.build`](#common.Tree^build). constructor( /// The type of the top node. type, /// This node's child nodes. children, /// The positions (offsets relative to the start of this tree) of /// the children. positions, /// The total length of this tree length, /// Per-node [node props](#common.NodeProp) to associate with this node. props) { this.type = type; this.children = children; this.positions = positions; this.length = length; /// @internal this.props = null; if (props && props.length) { this.props = Object.create(null); for (let [prop, value] of props) this.props[typeof prop == "number" ? prop : prop.id] = value; } } /// @internal toString() { let mounted = this.prop(NodeProp.mounted); if (mounted && !mounted.overlay) return mounted.tree.toString(); let children = ""; for (let ch of this.children) { let str = ch.toString(); if (str) { if (children) children += ","; children += str; } } return !this.type.name ? children : (/\W/.test(this.type.name) && !this.type.isError ? JSON.stringify(this.type.name) : this.type.name) + (children.length ? "(" + children + ")" : ""); } /// Get a [tree cursor](#common.TreeCursor) positioned at the top of /// the tree. Mode can be used to [control](#common.IterMode) which /// nodes the cursor visits. cursor(mode = 0) { return new TreeCursor(this.topNode, mode); } /// Get a [tree cursor](#common.TreeCursor) pointing into this tree /// at the given position and side (see /// [`moveTo`](#common.TreeCursor.moveTo). cursorAt(pos, side = 0, mode = 0) { let scope = CachedNode.get(this) || this.topNode; let cursor = new TreeCursor(scope); cursor.moveTo(pos, side); CachedNode.set(this, cursor._tree); return cursor; } /// Get a [syntax node](#common.SyntaxNode) object for the top of the /// tree. get topNode() { return new TreeNode(this, 0, 0, null); } /// Get the [syntax node](#common.SyntaxNode) at the given position. /// If `side` is -1, this will move into nodes that end at the /// position. If 1, it'll move into nodes that start at the /// position. With 0, it'll only enter nodes that cover the position /// from both sides. resolve(pos, side = 0) { let node = resolveNode(CachedNode.get(this) || this.topNode, pos, side, false); CachedNode.set(this, node); return node; } /// Like [`resolve`](#common.Tree.resolve), but will enter /// [overlaid](#common.MountedTree.overlay) nodes, producing a syntax node /// pointing into the innermost overlaid tree at the given position /// (with parent links going through all parent structure, including /// the host trees). resolveInner(pos, side = 0) { let node = resolveNode(CachedInnerNode.get(this) || this.topNode, pos, side, true); CachedInnerNode.set(this, node); return node; } /// Iterate over the tree and its children, calling `enter` for any /// node that touches the `from`/`to` region (if given) before /// running over such a node's children, and `leave` (if given) when /// leaving the node. When `enter` returns `false`, that node will /// not have its children iterated over (or `leave` called). iterate(spec) { let { enter, leave, from = 0, to = this.length } = spec; for (let c = this.cursor((spec.mode || 0) | IterMode.IncludeAnonymous);;) { let entered = false; if (c.from <= to && c.to >= from && (c.type.isAnonymous || enter(c) !== false)) { if (c.firstChild()) continue; entered = true; } for (;;) { if (entered && leave && !c.type.isAnonymous) leave(c); if (c.nextSibling()) break; if (!c.parent()) return; entered = true; } } } /// Get the value of the given [node prop](#common.NodeProp) for this /// node. Works with both per-node and per-type props. prop(prop) { return !prop.perNode ? this.type.prop(prop) : this.props ? this.props[prop.id] : undefined; } /// Returns the node's [per-node props](#common.NodeProp.perNode) in a /// format that can be passed to the [`Tree`](#common.Tree) /// constructor. get propValues() { let result = []; if (this.props) for (let id in this.props) result.push([+id, this.props[id]]); return result; } /// Balance the direct children of this tree, producing a copy of /// which may have children grouped into subtrees with type /// [`NodeType.none`](#common.NodeType^none). balance(config = {}) { return this.children.length <= 8 /* BranchFactor */ ? this : balanceRange(NodeType.none, this.children, this.positions, 0, this.children.length, 0, this.length, (children, positions, length) => new Tree(this.type, children, positions, length, this.propValues), config.makeTree || ((children, positions, length) => new Tree(NodeType.none, children, positions, length))); } /// Build a tree from a postfix-ordered buffer of node information, /// or a cursor over such a buffer. static build(data) { return buildTree(data); } } /// The empty tree Tree.empty = new Tree(NodeType.none, [], [], 0); class FlatBufferCursor { constructor(buffer, index) { this.buffer = buffer; this.index = index; } get id() { return this.buffer[this.index - 4]; } get start() { return this.buffer[this.index - 3]; } get end() { return this.buffer[this.index - 2]; } get size() { return this.buffer[this.index - 1]; } get pos() { return this.index; } next() { this.index -= 4; } fork() { return new FlatBufferCursor(this.buffer, this.index); } } /// Tree buffers contain (type, start, end, endIndex) quads for each /// node. In such a buffer, nodes are stored in prefix order (parents /// before children, with the endIndex of the parent indicating which /// children belong to it). class TreeBuffer { /// Create a tree buffer. constructor( /// The buffer's content. buffer, /// The total length of the group of nodes in the buffer. length, /// The node set used in this buffer. set) { this.buffer = buffer; this.length = length; this.set = set; } /// @internal get type() { return NodeType.none; } /// @internal toString() { let result = []; for (let index = 0; index < this.buffer.length;) { result.push(this.childString(index)); index = this.buffer[index + 3]; } return result.join(","); } /// @internal childString(index) { let id = this.buffer[index], endIndex = this.buffer[index + 3]; let type = this.set.types[id], result = type.name; if (/\W/.test(result) && !type.isError) result = JSON.stringify(result); index += 4; if (endIndex == index) return result; let children = []; while (index < endIndex) { children.push(this.childString(index)); index = this.buffer[index + 3]; } return result + "(" + children.join(",") + ")"; } /// @internal findChild(startIndex, endIndex, dir, pos, side) { let { buffer } = this, pick = -1; for (let i = startIndex; i != endIndex; i = buffer[i + 3]) { if (checkSide(side, pos, buffer[i + 1], buffer[i + 2])) { pick = i; if (dir > 0) break; } } return pick; } /// @internal slice(startI, endI, from, to) { let b = this.buffer; let copy = new Uint16Array(endI - startI); for (let i = startI, j = 0; i < endI;) { copy[j++] = b[i++]; copy[j++] = b[i++] - from; copy[j++] = b[i++] - from; copy[j++] = b[i++] - startI; } return new TreeBuffer(copy, to - from, this.set); } } function checkSide(side, pos, from, to) { switch (side) { case -2 /* Before */: return from < pos; case -1 /* AtOrBefore */: return to >= pos && from < pos; case 0 /* Around */: return from < pos && to > pos; case 1 /* AtOrAfter */: return from <= pos && to > pos; case 2 /* After */: return to > pos; case 4 /* DontCare */: return true; } } function enterUnfinishedNodesBefore(node, pos) { let scan = node.childBefore(pos); while (scan) { let last = scan.lastChild; if (!last || last.to != scan.to) break; if (last.type.isError && last.from == last.to) { node = scan; scan = last.prevSibling; } else { scan = last; } } return node; } function resolveNode(node, pos, side, overlays) { var _a; // Move up to a node that actually holds the position, if possible while (node.from == node.to || (side < 1 ? node.from >= pos : node.from > pos) || (side > -1 ? node.to <= pos : node.to < pos)) { let parent = !overlays && node instanceof TreeNode && node.index < 0 ? null : node.parent; if (!parent) return node; node = parent; } let mode = overlays ? 0 : IterMode.IgnoreOverlays; // Must go up out of overlays when those do not overlap with pos if (overlays) for (let scan = node, parent = scan.parent; parent; scan = parent, parent = scan.parent) { if (scan instanceof TreeNode && scan.index < 0 && ((_a = parent.enter(pos, side, mode)) === null || _a === void 0 ? void 0 : _a.from) != scan.from) node = parent; } for (;;) { let inner = node.enter(pos, side, mode); if (!inner) return node; node = inner; } } class TreeNode { constructor(_tree, from, // Index in parent node, set to -1 if the node is not a direct child of _parent.node (overlay) index, _parent) { this._tree = _tree; this.from = from; this.index = index; this._parent = _parent; } get type() { return this._tree.type; } get name() { return this._tree.type.name; } get to() { return this.from + this._tree.length; } nextChild(i, dir, pos, side, mode = 0) { for (let parent = this;;) { for (let { children, positions } = parent._tree, e = dir > 0 ? children.length : -1; i != e; i += dir) { let next = children[i], start = positions[i] + parent.from; if (!checkSide(side, pos, start, start + next.length)) continue; if (next instanceof TreeBuffer) { if (mode & IterMode.ExcludeBuffers) continue; let index = next.findChild(0, next.buffer.length, dir, pos - start, side); if (index > -1) return new BufferNode(new BufferContext(parent, next, i, start), null, index); } else if ((mode & IterMode.IncludeAnonymous) || (!next.type.isAnonymous || hasChild(next))) { let mounted; if (!(mode & IterMode.IgnoreMounts) && next.props && (mounted = next.prop(NodeProp.mounted)) && !mounted.overlay) return new TreeNode(mounted.tree, start, i, parent); let inner = new TreeNode(next, start, i, parent); return (mode & IterMode.IncludeAnonymous) || !inner.type.isAnonymous ? inner : inner.nextChild(dir < 0 ? next.children.length - 1 : 0, dir, pos, side); } } if ((mode & IterMode.IncludeAnonymous) || !parent.type.isAnonymous) return null; if (parent.index >= 0) i = parent.index + dir; else i = dir < 0 ? -1 : parent._parent._tree.children.length; parent = parent._parent; if (!parent) return null; } } get firstChild() { return this.nextChild(0, 1, 0, 4 /* DontCare */); } get lastChild() { return this.nextChild(this._tree.children.length - 1, -1, 0, 4 /* DontCare */); } childAfter(pos) { return this.nextChild(0, 1, pos, 2 /* After */); } childBefore(pos) { return this.nextChild(this._tree.children.length - 1, -1, pos, -2 /* Before */); } enter(pos, side, mode = 0) { let mounted; if (!(mode & IterMode.IgnoreOverlays) && (mounted = this._tree.prop(NodeProp.mounted)) && mounted.overlay) { let rPos = pos - this.from; for (let { from, to } of mounted.overlay) { if ((side > 0 ? from <= rPos : from < rPos) && (side < 0 ? to >= rPos : to > rPos)) return new TreeNode(mounted.tree, mounted.overlay[0].from + this.from, -1, this); } } return this.nextChild(0, 1, pos, side, mode); } nextSignificantParent() { let val = this; while (val.type.isAnonymous && val._parent) val = val._parent; return val; } get parent() { return this._parent ? this._parent.nextSignificantParent() : null; } get nextSibling() { return this._parent && this.index >= 0 ? this._parent.nextChild(this.index + 1, 1, 0, 4 /* DontCare */) : null; } get prevSibling() { return this._parent && this.index >= 0 ? this._parent.nextChild(this.index - 1, -1, 0, 4 /* DontCare */) : null; } cursor(mode = 0) { return new TreeCursor(this, mode); } get tree() { return this._tree; } toTree() { return this._tree; } resolve(pos, side = 0) { return resolveNode(this, pos, side, false); } resolveInner(pos, side = 0) { return resolveNode(this, pos, side, true); } enterUnfinishedNodesBefore(pos) { return enterUnfinishedNodesBefore(this, pos); } getChild(type, before = null, after = null) { let r = getChildren(this, type, before, after); return r.length ? r[0] : null; } getChildren(type, before = null, after = null) { return getChildren(this, type, before, after); } /// @internal toString() { return this._tree.toString(); } get node() { return this; } matchContext(context) { return matchNodeContext(this, context); } } function getChildren(node, type, before, after) { let cur = node.cursor(), result = []; if (!cur.firstChild()) return result; if (before != null) while (!cur.type.is(before)) if (!cur.nextSibling()) return result; for (;;) { if (after != null && cur.type.is(after)) return result; if (cur.type.is(type)) result.push(cur.node); if (!cur.nextSibling()) return after == null ? result : []; } } function matchNodeContext(node, context, i = context.length - 1) { for (let p = node.parent; i >= 0; p = p.parent) { if (!p) return false; if (!p.type.isAnonymous) { if (context[i] && context[i] != p.name) return false; i--; } } return true; } class BufferContext { constructor(parent, buffer, index, start) { this.parent = parent; this.buffer = buffer; this.index = index; this.start = start; } } class BufferNode { constructor(context, _parent, index) { this.context = context; this._parent = _parent; this.index = index; this.type = context.buffer.set.types[context.buffer.buffer[index]]; } get name() { return this.type.name; } get from() { return this.context.start + this.context.buffer.buffer[this.index + 1]; } get to() { return this.context.start + this.context.buffer.buffer[this.index + 2]; } child(dir, pos, side) { let { buffer } = this.context; let index = buffer.findChild(this.index + 4, buffer.buffer[this.index + 3], dir, pos - this.context.start, side); return index < 0 ? null : new BufferNode(this.context, this, index); } get firstChild() { return this.child(1, 0, 4 /* DontCare */); } get lastChild() { return this.child(-1, 0, 4 /* DontCare */); } childAfter(pos) { return this.child(1, pos, 2 /* After */); } childBefore(pos) { return this.child(-1, pos, -2 /* Before */); } enter(pos, side, mode = 0) { if (mode & IterMode.ExcludeBuffers) return null; let { buffer } = this.context; let index = buffer.findChild(this.index + 4, buffer.buffer[this.index + 3], side > 0 ? 1 : -1, pos - this.context.start, side); return index < 0 ? null : new BufferNode(this.context, this, index); } get parent() { return this._parent || this.context.parent.nextSignificantParent(); } externalSibling(dir) { return this._parent ? null : this.context.parent.nextChild(this.context.index + dir, dir, 0, 4 /* DontCare */); } get nextSibling() { let { buffer } = this.context; let after = buffer.buffer[this.index + 3]; if (after < (this._parent ? buffer.buffer[this._parent.index + 3] : buffer.buffer.length)) return new BufferNode(this.context, this._parent, after); return this.externalSibling(1); } get prevSibling() { let { buffer } = this.context; let parentStart = this._parent ? this._parent.index + 4 : 0; if (this.index == parentStart) return this.externalSibling(-1); return new BufferNode(this.context, this._parent, buffer.findChild(parentStart, this.index, -1, 0, 4 /* DontCare */)); } cursor(mode = 0) { return new TreeCursor(this, mode); } get tree() { return null; } toTree() { let children = [], positions = []; let { buffer } = this.context; let startI = this.index + 4, endI = buffer.buffer[this.index + 3]; if (endI > startI) { let from = buffer.buffer[this.index + 1], to = buffer.buffer[this.index + 2]; children.push(buffer.slice(startI, endI, from, to)); positions.push(0); } return new Tree(this.type, children, positions, this.to - this.from); } resolve(pos, side = 0) { return resolveNode(this, pos, side, false); } resolveInner(pos, side = 0) { return resolveNode(this, pos, side, true); } enterUnfinishedNodesBefore(pos) { return enterUnfinishedNodesBefore(this, pos); } /// @internal toString() { return this.context.buffer.childString(this.index); } getChild(type, before = null, after = null) { let r = getChildren(this, type, before, after); return r.length ? r[0] : null; } getChildren(type, before = null, after = null) { return getChildren(this, type, before, after); } get node() { return this; } matchContext(context) { return matchNodeContext(this, context); } } /// A tree cursor object focuses on a given node in a syntax tree, and /// allows you to move to adjacent nodes. class TreeCursor { /// @internal constructor(node, /// @internal mode = 0) { this.mode = mode; /// @internal this.buffer = null; this.stack = []; /// @internal this.index = 0; this.bufferNode = null; if (node instanceof TreeNode) { this.yieldNode(node); } else { this._tree = node.context.parent; this.buffer = node.context; for (let n = node._parent; n; n = n._parent) this.stack.unshift(n.index); this.bufferNode = node; this.yieldBuf(node.index); } } /// Shorthand for `.type.name`. get name() { return this.type.name; } yieldNode(node) { if (!node) return false; this._tree = node; this.type = node.type; this.from = node.from; this.to = node.to; return true; } yieldBuf(index, type) { this.index = index; let { start, buffer } = this.buffer; this.type = type || buffer.set.types[buffer.buffer[index]]; this.from = start + buffer.buffer[index + 1]; this.to = start + buffer.buffer[index + 2]; return true; } yield(node) { if (!node) return false; if (node instanceof TreeNode) { this.buffer = null; return this.yieldNode(node); } this.buffer = node.context; return this.yieldBuf(node.index, node.type); } /// @internal toString() { return this.buffer ? this.buffer.buffer.childString(this.index) : this._tree.toString(); } /// @internal enterChild(dir, pos, side) { if (!this.buffer) return this.yield(this._tree.nextChild(dir < 0 ? this._tree._tree.children.length - 1 : 0, dir, pos, side, this.mode)); let { buffer } = this.buffer; let index = buffer.findChild(this.index + 4, buffer.buffer[this.index + 3], dir, pos - this.buffer.start, side); if (index < 0) return false; this.stack.push(this.index); return this.yieldBuf(index); } /// Move the cursor to this node's first child. When this returns /// false, the node has no child, and the cursor has not been moved. firstChild() { return this.enterChild(1, 0, 4 /* DontCare */); } /// Move the cursor to this node's last child. lastChild() { return this.enterChild(-1, 0, 4 /* DontCare */); } /// Move the cursor to the first child that ends after `pos`. childAfter(pos) { return this.enterChild(1, pos, 2 /* After */); } /// Move to the last child that starts before `pos`. childBefore(pos) { return this.enterChild(-1, pos, -2 /* Before */); } /// Move the cursor to the child around `pos`. If side is -1 the /// child may end at that position, when 1 it may start there. This /// will also enter [overlaid](#common.MountedTree.overlay) /// [mounted](#common.NodeProp^mounted) trees unless `overlays` is /// set to false. enter(pos, side, mode = this.mode) { if (!this.buffer) return this.yield(this._tree.enter(pos, side, mode)); return mode & IterMode.ExcludeBuffers ? false : this.enterChild(1, pos, side); } /// Move to the node's parent node, if this isn't the top node. parent() { if (!this.buffer) return this.yieldNode((this.mode & IterMode.IncludeAnonymous) ? this._tree._parent : this._tree.parent); if (this.stack.length) return this.yieldBuf(this.stack.pop()); let parent = (this.mode & IterMode.IncludeAnonymous) ? this.buffer.parent : this.buffer.parent.nextSignificantParent(); this.buffer = null; return this.yieldNode(parent); } /// @internal sibling(dir) { if (!this.buffer) return !this._tree._parent ? false : this.yield(this._tree.index < 0 ? null : this._tree._parent.nextChild(this._tree.index + dir, dir, 0, 4 /* DontCare */, this.mode)); let { buffer } = this.buffer, d = this.stack.length - 1; if (dir < 0) { let parentStart = d < 0 ? 0 : this.stack[d] + 4; if (this.index != parentStart) return this.yieldBuf(buffer.findChild(parentStart, this.index, -1, 0, 4 /* DontCare */)); } else { let after = buffer.buffer[this.index + 3]; if (after < (d < 0 ? buffer.buffer.length : buffer.buffer[this.stack[d] + 3])) return this.yieldBuf(after); } return d < 0 ? this.yield(this.buffer.parent.nextChild(this.buffer.index + dir, dir, 0, 4 /* DontCare */, this.mode)) : false; } /// Move to this node's next sibling, if any. nextSibling() { return this.sibling(1); } /// Move to this node's previous sibling, if any. prevSibling() { return this.sibling(-1); } atLastNode(dir) { let index, parent, { buffer } = this; if (buffer) { if (dir > 0) { if (this.index < buffer.buffer.buffer.length) return false; } else { for (let i = 0; i < this.index; i++) if (buffer.buffer.buffer[i + 3] < this.index) return false; } ({ index, parent } = buffer); } else { ({ index, _parent: parent } = this._tree); } for (; parent; { index, _parent: parent } = parent) { if (index > -1) for (let i = index + dir, e = dir < 0 ? -1 : parent._tree.children.length; i != e; i += dir) { let child = parent._tree.children[i]; if ((this.mode & IterMode.IncludeAnonymous) || child instanceof TreeBuffer || !child.type.isAnonymous || hasChild(child)) return false; } } return true; } move(dir, enter) { if (enter && this.enterChild(dir, 0, 4 /* DontCare */)) return true; for (;;) { if (this.sibling(dir)) return true; if (this.atLastNode(dir) || !this.parent()) return false; } } /// Move to the next node in a /// [pre-order](https://en.wikipedia.org/wiki/Tree_traversal#Pre-order_(NLR)) /// traversal, going from a node to its first child or, if the /// current node is empty or `enter` is false, its next sibling or /// the next sibling of the first parent node that has one. next(enter = true) { return this.move(1, enter); } /// Move to the next node in a last-to-first pre-order traveral. A /// node is followed by its last child or, if it has none, its /// previous sibling or the previous sibling of the first parent /// node that has one. prev(enter = true) { return this.move(-1, enter); } /// Move the cursor to the innermost node that covers `pos`. If /// `side` is -1, it will enter nodes that end at `pos`. If it is 1, /// it will enter nodes that start at `pos`. moveTo(pos, side = 0) { // Move up to a node that actually holds the position, if possible while (this.from == this.to || (side < 1 ? this.from >= pos : this.from > pos) || (side > -1 ? this.to <= pos : this.to < pos)) if (!this.parent()) break; // Then scan down into child nodes as far as possible while (this.enterChild(1, pos, side)) { } return this; } /// Get a [syntax node](#common.SyntaxNode) at the cursor's current /// position. get node() { if (!this.buffer) return this._tree; let cache = this.bufferNode, result = null, depth = 0; if (cache && cache.context == this.buffer) { scan: for (let index = this.index, d = this.stack.length; d >= 0;) { for (let c = cache; c; c = c._parent) if (c.index == index) { if (index == this.index) return c; result = c; depth = d + 1; break scan; } index = this.stack[--d]; } } for (let i = depth; i < this.stack.length; i++) result = new BufferNode(this.buffer, result, this.stack[i]); return this.bufferNode = new BufferNode(this.buffer, result, this.index); } /// Get the [tree](#common.Tree) that represents the current node, if /// any. Will return null when the node is in a [tree /// buffer](#common.TreeBuffer). get tree() { return this.buffer ? null : this._tree._tree; } /// Iterate over the current node and all its descendants, calling /// `enter` when entering a node and `leave`, if given, when leaving /// one. When `enter` returns `false`, any children of that node are /// skipped, and `leave` isn't called for it. iterate(enter, leave) { for (let depth = 0;;) { let mustLeave = false; if (this.type.isAnonymous || enter(this) !== false) { if (this.firstChild()) { depth++; continue; } if (!this.type.isAnonymous) mustLeave = true; } for (;;) { if (mustLeave && leave) leave(this); mustLeave = this.type.isAnonymous; if (this.nextSibling()) break; if (!depth) return; this.parent(); depth--; mustLeave = true; } } } /// Test whether the current node matches a given context—a sequence /// of direct parent node names. Empty strings in the context array /// are treated as wildcards. matchContext(context) { if (!this.buffer) return matchNodeContext(this.node, context); let { buffer } = this.buffer, { types } = buffer.set; for (let i = context.length - 1, d = this.stack.length - 1; i >= 0; d--) { if (d < 0) return matchNodeContext(this.node, context, i); let type = types[buffer.buffer[this.stack[d]]]; if (!type.isAnonymous) { if (context[i] && context[i] != type.name) return false; i--; } } return true; } } function hasChild(tree) { return tree.children.some(ch => ch instanceof TreeBuffer || !ch.type.isAnonymous || hasChild(ch)); } function buildTree(data) { var _a; let { buffer, nodeSet, maxBufferLength = DefaultBufferLength, reused = [], minRepeatType = nodeSet.types.length } = data; let cursor = Array.isArray(buffer) ? new FlatBufferCursor(buffer, buffer.length) : buffer; let types = nodeSet.types; let contextHash = 0, lookAhead = 0; function takeNode(parentStart, minPos, children, positions, inRepeat) { let { id, start, end, size } = cursor; let lookAheadAtStart = lookAhead; while (size < 0) { cursor.next(); if (size == -1 /* Reuse */) { let node = reused[id]; children.push(node); positions.push(start - parentStart); return; } else if (size == -3 /* ContextChange */) { // Context change contextHash = id; return; } else if (size == -4 /* LookAhead */) { lookAhead = id; return; } else { throw new RangeError(`Unrecognized record size: ${size}`); } } let type = types[id], node, buffer; let startPos = start - parentStart; if (end - start <= maxBufferLength && (buffer = findBufferSize(cursor.pos - minPos, inRepeat))) { // Small enough for a buffer, and no reused nodes inside let data = new Uint16Array(buffer.size - buffer.skip); let endPos = cursor.pos - buffer.size, index = data.length; while (cursor.pos > endPos) index = copyToBuffer(buffer.start, data, index); node = new TreeBuffer(data, end - buffer.start, nodeSet); startPos = buffer.start - parentStart; } else { // Make it a node let endPos = cursor.pos - size; cursor.next(); let localChildren = [], localPositions = []; let localInRepeat = id >= minRepeatType ? id : -1; let lastGroup = 0, lastEnd = end; while (cursor.pos > endPos) { if (localInRepeat >= 0 && cursor.id == localInRepeat && cursor.size >= 0) { if (cursor.end <= lastEnd - maxBufferLength) { makeRepeatLeaf(localChildren, localPositions, start, lastGroup, cursor.end, lastEnd, localInRepeat, lookAheadAtStart); lastGroup = localChildren.length; lastEnd = cursor.end; } cursor.next(); } else { takeNode(start, endPos, localChildren, localPositions, localInRepeat); } } if (localInRepeat >= 0 && lastGroup > 0 && lastGroup < localChildren.length) makeRepeatLeaf(localChildren, localPositions, start, lastGroup, start, lastEnd, localInRepeat, lookAheadAtStart); localChildren.reverse(); localPositions.reverse(); if (localInRepeat > -1 && lastGroup > 0) { let make = makeBalanced(type); node = balanceRange(type, localChildren, localPositions, 0, localChildren.length, 0, end - start, make, make); } else { node = makeTree(type, localChildren, localPositions, end - start, lookAheadAtStart - end); } } children.push(node); positions.push(startPos); } function makeBalanced(type) { return (children, positions, length) => { let lookAhead = 0, lastI = children.length - 1, last, lookAheadProp; if (lastI >= 0 && (last = children[lastI]) instanceof Tree) { if (!lastI && last.type == type && last.length == length) return last; if (lookAheadProp = last.prop(NodeProp.lookAhead)) lookAhead = positions[lastI] + last.length + lookAheadProp; } return makeTree(type, children, positions, length, lookAhead); }; } function makeRepeatLeaf(children, positions, base, i, from, to, type, lookAhead) { let localChildren = [], localPositions = []; while (children.length > i) { localChildren.push(children.pop()); localPositions.push(positions.pop() + base - from); } children.push(makeTree(nodeSet.types[type], localChildren, localPositions, to - from, lookAhead - to)); positions.push(from - base); } function makeTree(type, children, positions, length, lookAhead = 0, props) { if (contextHash) { let pair = [NodeProp.contextHash, contextHash]; props = props ? [pair].concat(props) : [pair]; } if (lookAhead > 25) { let pair = [NodeProp.lookAhead, lookAhead]; props = props ? [pair].concat(props) : [pair]; } return new Tree(type, children, positions, length, props); } function findBufferSize(maxSize, inRepeat) { // Scan through the buffer to find previous siblings that fit // together in a TreeBuffer, and don't contain any reused nodes // (which can't be stored in a buffer). // If `inRepeat` is > -1, ignore node boundaries of that type for // nesting, but make sure the end falls either at the start // (`maxSize`) or before such a node. let fork = cursor.fork(); let size = 0, start = 0, skip = 0, minStart = fork.end - maxBufferLength; let result = { size: 0, start: 0, skip: 0 }; scan: for (let minPos = fork.pos - maxSize; fork.pos > minPos;) { let nodeSize = fork.size; // Pretend nested repeat nodes of the same type don't exist if (fork.id == inRepeat && nodeSize >= 0) { // Except that we store the current state as a valid return // value. result.size = size; result.start = start; result.skip = skip; skip += 4; size += 4; fork.next(); continue; } let startPos = fork.pos - nodeSize; if (nodeSize < 0 || startPos < minPos || fork.start < minStart) break; let localSkipped = fork.id >= minRepeatType ? 4 : 0; let nodeStart = fork.start; fork.next(); while (fork.pos > startPos) { if (fork.size < 0) { if (fork.size == -3 /* ContextChange */) localSkipped += 4; else break scan; } else if (fork.id >= minRepeatType) { localSkipped += 4; } fork.next(); } start = nodeStart; size += nodeSize; skip += localSkipped; } if (inRepeat < 0 || size == maxSize) { result.size = size; result.start = start; result.skip = skip; } return result.size > 4 ? result : undefined; } function copyToBuffer(bufferStart, buffer, index) { let { id, start, end, size } = cursor; cursor.next(); if (size >= 0 && id < minRepeatType) { let startIndex = index; if (size > 4) { let endPos = cursor.pos - (size - 4); while (cursor.pos > endPos) index = copyToBuffer(bufferStart, buffer, index); } buffer[--index] = startIndex; buffer[--index] = end - bufferStart; buffer[--index] = start - bufferStart; buffer[--index] = id; } else if (size == -3 /* ContextChange */) { contextHash = id; } else if (size == -4 /* LookAhead */) { lookAhead = id; } return index; } let children = [], positions = []; while (cursor.pos > 0) takeNode(data.start || 0, data.bufferStart || 0, children, positions, -1); let length = (_a = data.length) !== null && _a !== void 0 ? _a : (children.length ? positions[0] + children[0].length : 0); return new Tree(types[data.topID], children.reverse(), positions.reverse(), length); } const nodeSizeCache = new WeakMap; function nodeSize(balanceType, node) { if (!balanceType.isAnonymous || node instanceof TreeBuffer || node.type != balanceType) return 1; let size = nodeSizeCache.get(node); if (size == null) { size = 1; for (let child of node.children) { if (child.type != balanceType || !(child instanceof Tree)) { size = 1; break; } size += nodeSize(balanceType, child); } nodeSizeCache.set(node, size); } return size; } function balanceRange( // The type the balanced tree's inner nodes. balanceType, // The direct children and their positions children, positions, // The index range in children/positions to use from, to, // The start position of the nodes, relative to their parent. start, // Length of the outer node length, // Function to build the top node of the balanced tree mkTop, // Function to build internal nodes for the balanced tree mkTree) { let total = 0; for (let i = from; i < to; i++) total += nodeSize(balanceType, children[i]); let maxChild = Math.ceil((total * 1.5) / 8 /* BranchFactor */); let localChildren = [], localPositions = []; function divide(children, positions, from, to, offset) { for (let i = from; i < to;) { let groupFrom = i, groupStart = positions[i], groupSize = nodeSize(balanceType, children[i]); i++; for (; i < to; i++) { let nextSize = nodeSize(balanceType, children[i]); if (groupSize + nextSize >= maxChild) break; groupSize += nextSize; } if (i == groupFrom + 1) { if (groupSize > maxChild) { let only = children[groupFrom]; // Only trees can have a size > 1 divide(only.children, only.positions, 0, only.children.length, positions[groupFrom] + offset); continue; } localChildren.push(children[groupFrom]); } else { let length = positions[i - 1] + children[i - 1].length - groupStart; localChildren.push(balanceRange(balanceType, children, positions, groupFrom, i, groupStart, length, null, mkTree)); } localPositions.push(groupStart + offset - start); } } divide(children, positions, from, to, 0); return (mkTop || mkTree)(localChildren, localPositions, length); } /// Provides a way to associate values with pieces of trees. As long /// as that part of the tree is reused, the associated values can be /// retrieved from an updated tree. class NodeWeakMap { constructor() { this.map = new WeakMap(); } setBuffer(buffer, index, value) { let inner = this.map.get(buffer); if (!inner) this.map.set(buffer, inner = new Map); inner.set(index, value); } getBuffer(buffer, index) { let inner = this.map.get(buffer); return inner && inner.get(index); } /// Set the value for this syntax node. set(node, value) { if (node instanceof BufferNode) this.setBuffer(node.context.buffer, node.index, value); else if (node instanceof TreeNode) this.map.set(node.tree, value); } /// Retrieve value for this syntax node, if it exists in the map. get(node) { return node instanceof BufferNode ? this.getBuffer(node.context.buffer, node.index) : node instanceof TreeNode ? this.map.get(node.tree) : undefined; } /// Set the value for the node that a cursor currently points to. cursorSet(cursor, value) { if (cursor.buffer) this.setBuffer(cursor.buffer.buffer, cursor.index, value); else this.map.set(cursor.tree, value); } /// Retrieve the value for the node that a cursor currently points /// to. cursorGet(cursor) { return cursor.buffer ? this.getBuffer(cursor.buffer.buffer, cursor.index) : this.map.get(cursor.tree); } } /// Tree fragments are used during [incremental /// parsing](#common.Parser.startParse) to track parts of old trees /// that can be reused in a new parse. An array of fragments is used /// to track regions of an old tree whose nodes might be reused in new /// parses. Use the static /// [`applyChanges`](#common.TreeFragment^applyChanges) method to /// update fragments for document changes. class TreeFragment { /// Construct a tree fragment. You'll usually want to use /// [`addTree`](#common.TreeFragment^addTree) and /// [`applyChanges`](#common.TreeFragment^applyChanges) instead of /// calling this directly. constructor( /// The start of the unchanged range pointed to by this fragment. /// This refers to an offset in the _updated_ document (as opposed /// to the original tree). from, /// The end of the unchanged range. to, /// The tree that this fragment is based on. tree, /// The offset between the fragment's tree and the document that /// this fragment can be used against. Add this when going from /// document to tree positions, subtract it to go from tree to /// document positions. offset, openStart = false, openEnd = false) { this.from = from; this.to = to; this.tree = tree; this.offset = offset; this.open = (openStart ? 1 /* Start */ : 0) | (openEnd ? 2 /* End */ : 0); } /// Whether the start of the fragment represents the start of a /// parse, or the end of a change. (In the second case, it may not /// be safe to reuse some nodes at the start, depending on the /// parsing algorithm.) get openStart() { return (this.open & 1 /* Start */) > 0; } /// Whether the end of the fragment represents the end of a /// full-document parse, or the start of a change. get openEnd() { return (this.open & 2 /* End */) > 0; } /// Create a set of fragments from a freshly parsed tree, or update /// an existing set of fragments by replacing the ones that overlap /// with a tree with content from the new tree. When `partial` is /// true, the parse is treated as incomplete, and the resulting /// fragment has [`openEnd`](#common.TreeFragment.openEnd) set to /// true. static addTree(tree, fragments = [], partial = false) { let result = [new TreeFragment(0, tree.length, tree, 0, false, partial)]; for (let f of fragments) if (f.to > tree.length) result.push(f); return result; } /// Apply a set of edits to an array of fragments, removing or /// splitting fragments as necessary to remove edited ranges, and /// adjusting offsets for fragments that moved. static applyChanges(fragments, changes, minGap = 128) { if (!changes.length) return fragments; let result = []; let fI = 1, nextF = fragments.length ? fragments[0] : null; for (let cI = 0, pos = 0, off = 0;; cI++) { let nextC = cI < changes.length ? changes[cI] : null; let nextPos = nextC ? nextC.fromA : 1e9; if (nextPos - pos >= minGap) while (nextF && nextF.from < nextPos) { let cut = nextF; if (pos >= cut.from || nextPos <= cut.to || off) { let fFrom = Math.max(cut.from, pos) - off, fTo = Math.min(cut.to, nextPos) - off; cut = fFrom >= fTo ? null : new TreeFragment(fFrom, fTo, cut.tree, cut.offset + off, cI > 0, !!nextC); } if (cut) result.push(cut); if (nextF.to > nextPos) break; nextF = fI < fragments.length ? fragments[fI++] : null; } if (!nextC) break; pos = nextC.toA; off = nextC.toA - nextC.toB; } return result; } } /// A superclass that parsers should extend. class Parser { /// Start a parse, returning a [partial parse](#common.PartialParse) /// object. [`fragments`](#common.TreeFragment) can be passed in to /// make the parse incremental. /// /// By default, the entire input is parsed. You can pass `ranges`, /// which should be a sorted array of non-empty, non-overlapping /// ranges, to parse only those ranges. The tree returned in that /// case will start at `ranges[0].from`. startParse(input, fragments, ranges) { if (typeof input == "string") input = new StringInput(input); ranges = !ranges ? [new Range(0, input.length)] : ranges.length ? ranges.map(r => new Range(r.from, r.to)) : [new Range(0, 0)]; return this.createParse(input, fragments || [], ranges); } /// Run a full parse, returning the resulting tree. parse(input, fragments, ranges) { let parse = this.startParse(input, fragments, ranges); for (;;) { let done = parse.advance(); if (done) return done; } } } class StringInput { constructor(string) { this.string = string; } get length() { return this.string.length; } chunk(from) { return this.string.slice(from); } get lineChunks() { return false; } read(from, to) { return this.string.slice(from, to); } } /// Create a parse wrapper that, after the inner parse completes, /// scans its tree for mixed language regions with the `nest` /// function, runs the resulting [inner parses](#common.NestedParse), /// and then [mounts](#common.NodeProp^mounted) their results onto the /// tree. function parseMixed(nest) { return (parse, input, fragments, ranges) => new MixedParse(parse, nest, input, fragments, ranges); } class InnerParse { constructor(parser, parse, overlay, target, ranges) { this.parser = parser; this.parse = parse; this.overlay = overlay; this.target = target; this.ranges = ranges; } } class ActiveOverlay { constructor(parser, predicate, mounts, index, start, target, prev) { this.parser = parser; this.predicate = predicate; this.mounts = mounts; this.index = index; this.start = start; this.target = target; this.prev = prev; this.depth = 0; this.ranges = []; } } const stoppedInner = new NodeProp({ perNode: true }); class MixedParse { constructor(base, nest, input, fragments, ranges) { this.nest = nest; this.input = input; this.fragments = fragments; this.ranges = ranges; this.inner = []; this.innerDone = 0; this.baseTree = null; this.stoppedAt = null; this.baseParse = base; } advance() { if (this.baseParse) { let done = this.baseParse.advance(); if (!done) return null; this.baseParse = null; this.baseTree = done; this.startInner(); if (this.stoppedAt != null) for (let inner of this.inner) inner.parse.stopAt(this.stoppedAt); } if (this.innerDone == this.inner.length) { let result = this.baseTree; if (this.stoppedAt != null) result = new Tree(result.type, result.children, result.positions, result.length, result.propValues.concat([[stoppedInner, this.stoppedAt]])); return result; } let inner = this.inner[this.innerDone], done = inner.parse.advance(); if (done) { this.innerDone++; // This is a somewhat dodgy but super helpful hack where we // patch up nodes created by the inner parse (and thus // presumably not aliased anywhere else) to hold the information // about the inner parse. let props = Object.assign(Object.create(null), inner.target.props); props[NodeProp.mounted.id] = new MountedTree(done, inner.overlay, inner.parser); inner.target.props = props; } return null; } get parsedPos() { if (this.baseParse) return 0; let pos = this.input.length; for (let i = this.innerDone; i < this.inner.length; i++) { if (this.inner[i].ranges[0].from < pos) pos = Math.min(pos, this.inner[i].parse.parsedPos); } return pos; } stopAt(pos) { this.stoppedAt = pos; if (this.baseParse) this.baseParse.stopAt(pos); else for (let i = this.innerDone; i < this.inner.length; i++) this.inner[i].parse.stopAt(pos); } startInner() { let fragmentCursor = new FragmentCursor(this.fragments); let overlay = null; let covered = null; let cursor = new TreeCursor(new TreeNode(this.baseTree, this.ranges[0].from, 0, null), IterMode.IncludeAnonymous | IterMode.IgnoreMounts); scan: for (let nest, isCovered; this.stoppedAt == null || cursor.from < this.stoppedAt;) { let enter = true, range; if (fragmentCursor.hasNode(cursor)) { if (overlay) { let match = overlay.mounts.find(m => m.frag.from <= cursor.from && m.frag.to >= cursor.to && m.mount.overlay); if (match) for (let r of match.mount.overlay) { let from = r.from + match.pos, to = r.to + match.pos; if (from >= cursor.from && to <= cursor.to && !overlay.ranges.some(r => r.from < to && r.to > from)) overlay.ranges.push({ from, to }); } } enter = false; } else if (covered && (isCovered = checkCover(covered.ranges, cursor.from, cursor.to))) { enter = isCovered != 2 /* Full */; } else if (!cursor.type.isAnonymous && cursor.from < cursor.to && (nest = this.nest(cursor, this.input))) { if (!cursor.tree) materialize(cursor); let oldMounts = fragmentCursor.findMounts(cursor.from, nest.parser); if (typeof nest.overlay == "function") { overlay = new ActiveOverlay(nest.parser, nest.overlay, oldMounts, this.inner.length, cursor.from, cursor.tree, overlay); } else { let ranges = punchRanges(this.ranges, nest.overlay || [new Range(cursor.from, cursor.to)]); if (ranges.length) this.inner.push(new InnerParse(nest.parser, nest.parser.startParse(this.input, enterFragments(oldMounts, ranges), ranges), nest.overlay ? nest.overlay.map(r => new Range(r.from - cursor.from, r.to - cursor.from)) : null, cursor.tree, ranges)); if (!nest.overlay) enter = false; else if (ranges.length) covered = { ranges, depth: 0, prev: covered }; } } else if (overlay && (range = overlay.predicate(cursor))) { if (range === true) range = new Range(cursor.from, cursor.to); if (range.from < range.to) overlay.ranges.push(range); } if (enter && cursor.firstChild()) { if (overlay) overlay.depth++; if (covered) covered.depth++; } else { for (;;) { if (cursor.nextSibling()) break; if (!cursor.parent()) break scan; if (overlay && !--overlay.depth) { let ranges = punchRanges(this.ranges, overlay.ranges); if (ranges.length) this.inner.splice(overlay.index, 0, new InnerParse(overlay.parser, overlay.parser.startParse(this.input, enterFragments(overlay.mounts, ranges), ranges), overlay.ranges.map(r => new Range(r.from - overlay.start, r.to - overlay.start)), overlay.target, ranges)); overlay = overlay.prev; } if (covered && !--covered.depth) covered = covered.prev; } } } } } function checkCover(covered, from, to) { for (let range of covered) { if (range.from >= to) break; if (range.to > from) return range.from <= from && range.to >= to ? 2 /* Full */ : 1 /* Partial */; } return 0 /* None */; } // Take a piece of buffer and convert it into a stand-alone // TreeBuffer. function sliceBuf(buf, startI, endI, nodes, positions, off) { if (startI < endI) { let from = buf.buffer[startI + 1], to = buf.buffer[endI - 2]; nodes.push(buf.slice(startI, endI, from, to)); positions.push(from - off); } } // This function takes a node that's in a buffer, and converts it, and // its parent buffer nodes, into a Tree. This is again acting on the // assumption that the trees and buffers have been constructed by the // parse that was ran via the mix parser, and thus aren't shared with // any other code, making violations of the immutability safe. function materialize(cursor) { let { node } = cursor, depth = 0; // Scan up to the nearest tree do { cursor.parent(); depth++; } while (!cursor.tree); // Find the index of the buffer in that tree let i = 0, base = cursor.tree, off = 0; for (;; i++) { off = base.positions[i] + cursor.from; if (off <= node.from && off + base.children[i].length >= node.to) break; } let buf = base.children[i], b = buf.buffer; // Split a level in the buffer, putting the nodes before and after // the child that contains `node` into new buffers. function split(startI, endI, type, innerOffset, length) { let i = startI; while (b[i + 2] + off <= node.from) i = b[i + 3]; let children = [], positions = []; sliceBuf(buf, startI, i, children, positions, innerOffset); let from = b[i + 1], to = b[i + 2]; let isTarget = from + off == node.from && to + off == node.to && b[i] == node.type.id; children.push(isTarget ? node.toTree() : split(i + 4, b[i + 3], buf.set.types[b[i]], from, to - from)); positions.push(from - innerOffset); sliceBuf(buf, b[i + 3], endI, children, positions, innerOffset); return new Tree(type, children, positions, length); } base.children[i] = split(0, b.length, NodeType.none, 0, buf.length); // Move the cursor back to the target node for (let d = 0; d <= depth; d++) cursor.childAfter(node.from); } class StructureCursor { constructor(root, offset) { this.offset = offset; this.done = false; this.cursor = root.cursor(IterMode.IncludeAnonymous | IterMode.IgnoreMounts); } // Move to the first node (in pre-order) that starts at or after `pos`. moveTo(pos) { let { cursor } = this, p = pos - this.offset; while (!this.done && cursor.from < p) { if (cursor.to >= pos && cursor.enter(p, 1, IterMode.IgnoreOverlays | IterMode.ExcludeBuffers)) ; else if (!cursor.next(false)) this.done = true; } } hasNode(cursor) { this.moveTo(cursor.from); if (!this.done && this.cursor.from + this.offset == cursor.from && this.cursor.tree) { for (let tree = this.cursor.tree;;) { if (tree == cursor.tree) return true; if (tree.children.length && tree.positions[0] == 0 && tree.children[0] instanceof Tree) tree = tree.children[0]; else break; } } return false; } } class FragmentCursor { constructor(fragments) { var _a; this.fragments = fragments; this.curTo = 0; this.fragI = 0; if (fragments.length) { let first = this.curFrag = fragments[0]; this.curTo = (_a = first.tree.prop(stoppedInner)) !== null && _a !== void 0 ? _a : first.to; this.inner = new StructureCursor(first.tree, -first.offset); } else { this.curFrag = this.inner = null; } } hasNode(node) { while (this.curFrag && node.from >= this.curTo) this.nextFrag(); return this.curFrag && this.curFrag.from <= node.from && this.curTo >= node.to && this.inner.hasNode(node); } nextFrag() { var _a; this.fragI++; if (this.fragI == this.fragments.length) { this.curFrag = this.inner = null; } else { let frag = this.curFrag = this.fragments[this.fragI]; this.curTo = (_a = frag.tree.prop(stoppedInner)) !== null && _a !== void 0 ? _a : frag.to; this.inner = new StructureCursor(frag.tree, -frag.offset); } } findMounts(pos, parser) { var _a; let result = []; if (this.inner) { this.inner.cursor.moveTo(pos, 1); for (let pos = this.inner.cursor.node; pos; pos = pos.parent) { let mount = (_a = pos.tree) === null || _a === void 0 ? void 0 : _a.prop(NodeProp.mounted); if (mount && mount.parser == parser) { for (let i = this.fragI; i < this.fragments.length; i++) { let frag = this.fragments[i]; if (frag.from >= pos.to) break; if (frag.tree == this.curFrag.tree) result.push({ frag, pos: pos.from - frag.offset, mount }); } } } } return result; } } function punchRanges(outer, ranges) { let copy = null, current = ranges; for (let i = 1, j = 0; i < outer.length; i++) { let gapFrom = outer[i - 1].to, gapTo = outer[i].from; for (; j < current.length; j++) { let r = current[j]; if (r.from >= gapTo) break; if (r.to <= gapFrom) continue; if (!copy) current = copy = ranges.slice(); if (r.from < gapFrom) { copy[j] = new Range(r.from, gapFrom); if (r.to > gapTo) copy.splice(j + 1, 0, new Range(gapTo, r.to)); } else if (r.to > gapTo) { copy[j--] = new Range(gapTo, r.to); } else { copy.splice(j--, 1); } } } return current; } function findCoverChanges(a, b, from, to) { let iA = 0, iB = 0, inA = false, inB = false, pos = -1e9; let result = []; for (;;) { let nextA = iA == a.length ? 1e9 : inA ? a[iA].to : a[iA].from; let nextB = iB == b.length ? 1e9 : inB ? b[iB].to : b[iB].from; if (inA != inB) { let start = Math.max(pos, from), end = Math.min(nextA, nextB, to); if (start < end) result.push(new Range(start, end)); } pos = Math.min(nextA, nextB); if (pos == 1e9) break; if (nextA == pos) { if (!inA) inA = true; else { inA = false; iA++; } } if (nextB == pos) { if (!inB) inB = true; else { inB = false; iB++; } } } return result; } // Given a number of fragments for the outer tree, and a set of ranges // to parse, find fragments for inner trees mounted around those // ranges, if any. function enterFragments(mounts, ranges) { let result = []; for (let { pos, mount, frag } of mounts) { let startPos = pos + (mount.overlay ? mount.overlay[0].from : 0), endPos = startPos + mount.tree.length; let from = Math.max(frag.from, startPos), to = Math.min(frag.to, endPos); if (mount.overlay) { let overlay = mount.overlay.map(r => new Range(r.from + pos, r.to + pos)); let changes = findCoverChanges(ranges, overlay, from, to); for (let i = 0, pos = from;; i++) { let last = i == changes.length, end = last ? to : changes[i].from; if (end > pos) result.push(new TreeFragment(pos, end, mount.tree, -startPos, frag.from >= pos, frag.to <= end)); if (last) break; pos = changes[i].to; } } else { result.push(new TreeFragment(from, to, mount.tree, -startPos, frag.from >= startPos, frag.to <= endPos)); } } return result; } export { DefaultBufferLength, IterMode, MountedTree, NodeProp, NodeSet, NodeType, NodeWeakMap, Parser, Tree, TreeBuffer, TreeCursor, TreeFragment, parseMixed };