|
|
|
|
|
|
|
const DefaultBufferLength = 1024; |
|
let nextPropID = 0; |
|
class Range { |
|
constructor(from, to) { |
|
this.from = from; |
|
this.to = to; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
class NodeProp { |
|
|
|
|
|
|
|
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"); |
|
}); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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]; |
|
}; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
NodeProp.closedBy = new NodeProp({ deserialize: str => str.split(" ") }); |
|
|
|
|
|
|
|
|
|
|
|
NodeProp.openedBy = new NodeProp({ deserialize: str => str.split(" ") }); |
|
|
|
|
|
|
|
|
|
|
|
NodeProp.group = new NodeProp({ deserialize: str => str.split(" ") }); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NodeProp.isolate = new NodeProp({ deserialize: value => { |
|
if (value && value != "rtl" && value != "ltr" && value != "auto") |
|
throw new RangeError("Invalid value for isolate: " + value); |
|
return value || "auto"; |
|
} }); |
|
|
|
|
|
|
|
|
|
|
|
NodeProp.contextHash = new NodeProp({ perNode: true }); |
|
|
|
|
|
|
|
|
|
|
|
|
|
NodeProp.lookAhead = new NodeProp({ perNode: true }); |
|
|
|
|
|
|
|
|
|
|
|
NodeProp.mounted = new NodeProp({ perNode: true }); |
|
|
|
|
|
|
|
|
|
|
|
class MountedTree { |
|
constructor( |
|
|
|
|
|
|
|
tree, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
overlay, |
|
|
|
|
|
|
|
parser) { |
|
this.tree = tree; |
|
this.overlay = overlay; |
|
this.parser = parser; |
|
} |
|
|
|
|
|
|
|
static get(tree) { |
|
return tree && tree.props && tree.props[NodeProp.mounted.id]; |
|
} |
|
} |
|
const noProps = Object.create(null); |
|
|
|
|
|
|
|
class NodeType { |
|
|
|
|
|
|
|
constructor( |
|
|
|
|
|
|
|
|
|
|
|
|
|
name, |
|
|
|
|
|
|
|
props, |
|
|
|
|
|
|
|
|
|
id, |
|
|
|
|
|
|
|
flags = 0) { |
|
this.name = name; |
|
this.props = props; |
|
this.id = id; |
|
this.flags = flags; |
|
} |
|
|
|
|
|
|
|
static define(spec) { |
|
let props = spec.props && spec.props.length ? Object.create(null) : noProps; |
|
let flags = (spec.top ? 1 : 0) | (spec.skipped ? 2 : 0) | |
|
(spec.error ? 4 : 0) | (spec.name == null ? 8 : 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; |
|
} |
|
|
|
|
|
|
|
|
|
prop(prop) { return this.props[prop.id]; } |
|
|
|
|
|
|
|
get isTop() { return (this.flags & 1 ) > 0; } |
|
|
|
|
|
|
|
get isSkipped() { return (this.flags & 2 ) > 0; } |
|
|
|
|
|
|
|
get isError() { return (this.flags & 4 ) > 0; } |
|
|
|
|
|
|
|
|
|
get isAnonymous() { return (this.flags & 8 ) > 0; } |
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
}; |
|
} |
|
} |
|
|
|
|
|
|
|
NodeType.none = new NodeType("", Object.create(null), 0, 8 ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class NodeSet { |
|
|
|
|
|
|
|
|
|
constructor( |
|
|
|
|
|
|
|
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"); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
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(); |
|
|
|
|
|
|
|
|
|
var IterMode; |
|
(function (IterMode) { |
|
|
|
|
|
|
|
|
|
|
|
IterMode[IterMode["ExcludeBuffers"] = 1] = "ExcludeBuffers"; |
|
|
|
|
|
|
|
|
|
|
|
IterMode[IterMode["IncludeAnonymous"] = 2] = "IncludeAnonymous"; |
|
|
|
|
|
|
|
|
|
|
|
IterMode[IterMode["IgnoreMounts"] = 4] = "IgnoreMounts"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
IterMode[IterMode["IgnoreOverlays"] = 8] = "IgnoreOverlays"; |
|
})(IterMode || (IterMode = {})); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Tree { |
|
|
|
|
|
|
|
constructor( |
|
|
|
|
|
|
|
type, |
|
|
|
|
|
|
|
children, |
|
|
|
|
|
|
|
|
|
positions, |
|
|
|
|
|
|
|
length, |
|
|
|
|
|
|
|
props) { |
|
this.type = type; |
|
this.children = children; |
|
this.positions = positions; |
|
this.length = length; |
|
|
|
|
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
|
|
toString() { |
|
let mounted = MountedTree.get(this); |
|
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 + ")" : ""); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
cursor(mode = 0) { |
|
return new TreeCursor(this.topNode, mode); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
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 topNode() { |
|
return new TreeNode(this, 0, 0, null); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
resolve(pos, side = 0) { |
|
let node = resolveNode(CachedNode.get(this) || this.topNode, pos, side, false); |
|
CachedNode.set(this, node); |
|
return node; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
resolveInner(pos, side = 0) { |
|
let node = resolveNode(CachedInnerNode.get(this) || this.topNode, pos, side, true); |
|
CachedInnerNode.set(this, node); |
|
return node; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
resolveStack(pos, side = 0) { |
|
return stackIterator(this, pos, side); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
iterate(spec) { |
|
let { enter, leave, from = 0, to = this.length } = spec; |
|
let mode = spec.mode || 0, anon = (mode & IterMode.IncludeAnonymous) > 0; |
|
for (let c = this.cursor(mode | IterMode.IncludeAnonymous);;) { |
|
let entered = false; |
|
if (c.from <= to && c.to >= from && (!anon && c.type.isAnonymous || enter(c) !== false)) { |
|
if (c.firstChild()) |
|
continue; |
|
entered = true; |
|
} |
|
for (;;) { |
|
if (entered && leave && (anon || !c.type.isAnonymous)) |
|
leave(c); |
|
if (c.nextSibling()) |
|
break; |
|
if (!c.parent()) |
|
return; |
|
entered = true; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
prop(prop) { |
|
return !prop.perNode ? this.type.prop(prop) : this.props ? this.props[prop.id] : undefined; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
get propValues() { |
|
let result = []; |
|
if (this.props) |
|
for (let id in this.props) |
|
result.push([+id, this.props[id]]); |
|
return result; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
balance(config = {}) { |
|
return this.children.length <= 8 ? 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))); |
|
} |
|
|
|
|
|
|
|
|
|
static build(data) { return buildTree(data); } |
|
} |
|
|
|
|
|
|
|
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); } |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
class TreeBuffer { |
|
|
|
|
|
|
|
constructor( |
|
|
|
|
|
|
|
buffer, |
|
|
|
|
|
|
|
length, |
|
|
|
|
|
|
|
set) { |
|
this.buffer = buffer; |
|
this.length = length; |
|
this.set = set; |
|
} |
|
|
|
|
|
|
|
get type() { return NodeType.none; } |
|
|
|
|
|
|
|
toString() { |
|
let result = []; |
|
for (let index = 0; index < this.buffer.length;) { |
|
result.push(this.childString(index)); |
|
index = this.buffer[index + 3]; |
|
} |
|
return result.join(","); |
|
} |
|
|
|
|
|
|
|
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(",") + ")"; |
|
} |
|
|
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
slice(startI, endI, from) { |
|
let b = this.buffer; |
|
let copy = new Uint16Array(endI - startI), len = 0; |
|
for (let i = startI, j = 0; i < endI;) { |
|
copy[j++] = b[i++]; |
|
copy[j++] = b[i++] - from; |
|
let to = copy[j++] = b[i++] - from; |
|
copy[j++] = b[i++] - startI; |
|
len = Math.max(len, to); |
|
} |
|
return new TreeBuffer(copy, len, this.set); |
|
} |
|
} |
|
function checkSide(side, pos, from, to) { |
|
switch (side) { |
|
case -2 : return from < pos; |
|
case -1 : return to >= pos && from < pos; |
|
case 0 : return from < pos && to > pos; |
|
case 1 : return from <= pos && to > pos; |
|
case 2 : return to > pos; |
|
case 4 : return true; |
|
} |
|
} |
|
function resolveNode(node, pos, side, overlays) { |
|
var _a; |
|
|
|
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; |
|
|
|
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 BaseNode { |
|
cursor(mode = 0) { return new TreeCursor(this, mode); } |
|
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); |
|
} |
|
resolve(pos, side = 0) { |
|
return resolveNode(this, pos, side, false); |
|
} |
|
resolveInner(pos, side = 0) { |
|
return resolveNode(this, pos, side, true); |
|
} |
|
matchContext(context) { |
|
return matchNodeContext(this, context); |
|
} |
|
enterUnfinishedNodesBefore(pos) { |
|
let scan = this.childBefore(pos), node = this; |
|
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; |
|
} |
|
get node() { return this; } |
|
get next() { return this.parent; } |
|
} |
|
class TreeNode extends BaseNode { |
|
constructor(_tree, from, |
|
|
|
index, _parent) { |
|
super(); |
|
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) && (mounted = MountedTree.get(next)) && !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 ); } |
|
get lastChild() { return this.nextChild(this._tree.children.length - 1, -1, 0, 4 ); } |
|
childAfter(pos) { return this.nextChild(0, 1, pos, 2 ); } |
|
childBefore(pos) { return this.nextChild(this._tree.children.length - 1, -1, pos, -2 ); } |
|
enter(pos, side, mode = 0) { |
|
let mounted; |
|
if (!(mode & IterMode.IgnoreOverlays) && (mounted = MountedTree.get(this._tree)) && 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 ) : null; |
|
} |
|
get prevSibling() { |
|
return this._parent && this.index >= 0 ? this._parent.nextChild(this.index - 1, -1, 0, 4 ) : null; |
|
} |
|
get tree() { return this._tree; } |
|
toTree() { return this._tree; } |
|
|
|
|
|
|
|
toString() { return this._tree.toString(); } |
|
} |
|
function getChildren(node, type, before, after) { |
|
let cur = node.cursor(), result = []; |
|
if (!cur.firstChild()) |
|
return result; |
|
if (before != null) |
|
for (let found = false; !found;) { |
|
found = 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 extends BaseNode { |
|
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]; } |
|
constructor(context, _parent, index) { |
|
super(); |
|
this.context = context; |
|
this._parent = _parent; |
|
this.index = index; |
|
this.type = context.buffer.set.types[context.buffer.buffer[index]]; |
|
} |
|
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 ); } |
|
get lastChild() { return this.child(-1, 0, 4 ); } |
|
childAfter(pos) { return this.child(1, pos, 2 ); } |
|
childBefore(pos) { return this.child(-1, pos, -2 ); } |
|
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 ); |
|
} |
|
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 )); |
|
} |
|
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]; |
|
children.push(buffer.slice(startI, endI, from)); |
|
positions.push(0); |
|
} |
|
return new Tree(this.type, children, positions, this.to - this.from); |
|
} |
|
|
|
|
|
|
|
toString() { return this.context.buffer.childString(this.index); } |
|
} |
|
function iterStack(heads) { |
|
if (!heads.length) |
|
return null; |
|
let pick = 0, picked = heads[0]; |
|
for (let i = 1; i < heads.length; i++) { |
|
let node = heads[i]; |
|
if (node.from > picked.from || node.to < picked.to) { |
|
picked = node; |
|
pick = i; |
|
} |
|
} |
|
let next = picked instanceof TreeNode && picked.index < 0 ? null : picked.parent; |
|
let newHeads = heads.slice(); |
|
if (next) |
|
newHeads[pick] = next; |
|
else |
|
newHeads.splice(pick, 1); |
|
return new StackIterator(newHeads, picked); |
|
} |
|
class StackIterator { |
|
constructor(heads, node) { |
|
this.heads = heads; |
|
this.node = node; |
|
} |
|
get next() { return iterStack(this.heads); } |
|
} |
|
function stackIterator(tree, pos, side) { |
|
let inner = tree.resolveInner(pos, side), layers = null; |
|
for (let scan = inner instanceof TreeNode ? inner : inner.context.parent; scan; scan = scan.parent) { |
|
if (scan.index < 0) { |
|
let parent = scan.parent; |
|
(layers || (layers = [inner])).push(parent.resolve(pos, side)); |
|
scan = parent; |
|
} |
|
else { |
|
let mount = MountedTree.get(scan.tree); |
|
|
|
if (mount && mount.overlay && mount.overlay[0].from <= pos && mount.overlay[mount.overlay.length - 1].to >= pos) { |
|
let root = new TreeNode(mount.tree, mount.overlay[0].from + scan.from, -1, scan); |
|
(layers || (layers = [inner])).push(resolveNode(root, pos, side, false)); |
|
} |
|
} |
|
} |
|
return layers ? iterStack(layers) : inner; |
|
} |
|
|
|
|
|
|
|
|
|
class TreeCursor { |
|
|
|
|
|
|
|
get name() { return this.type.name; } |
|
|
|
|
|
|
|
constructor(node, |
|
|
|
|
|
|
|
mode = 0) { |
|
this.mode = mode; |
|
|
|
|
|
|
|
this.buffer = null; |
|
this.stack = []; |
|
|
|
|
|
|
|
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); |
|
} |
|
} |
|
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); |
|
} |
|
|
|
|
|
|
|
toString() { |
|
return this.buffer ? this.buffer.buffer.childString(this.index) : this._tree.toString(); |
|
} |
|
|
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
firstChild() { return this.enterChild(1, 0, 4 ); } |
|
|
|
|
|
|
|
lastChild() { return this.enterChild(-1, 0, 4 ); } |
|
|
|
|
|
|
|
childAfter(pos) { return this.enterChild(1, pos, 2 ); } |
|
|
|
|
|
|
|
childBefore(pos) { return this.enterChild(-1, pos, -2 ); } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
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 , 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 )); |
|
} |
|
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 , this.mode)) : false; |
|
} |
|
|
|
|
|
|
|
nextSibling() { return this.sibling(1); } |
|
|
|
|
|
|
|
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 )) |
|
return true; |
|
for (;;) { |
|
if (this.sibling(dir)) |
|
return true; |
|
if (this.atLastNode(dir) || !this.parent()) |
|
return false; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
next(enter = true) { return this.move(1, enter); } |
|
|
|
|
|
|
|
|
|
|
|
|
|
prev(enter = true) { return this.move(-1, enter); } |
|
|
|
|
|
|
|
|
|
|
|
moveTo(pos, side = 0) { |
|
|
|
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; |
|
|
|
while (this.enterChild(1, pos, side)) { } |
|
return this; |
|
} |
|
|
|
|
|
|
|
|
|
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 tree() { |
|
return this.buffer ? null : this._tree._tree; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
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, depth) { |
|
let { id, start, end, size } = cursor; |
|
let lookAheadAtStart = lookAhead; |
|
while (size < 0) { |
|
cursor.next(); |
|
if (size == -1 ) { |
|
let node = reused[id]; |
|
children.push(node); |
|
positions.push(start - parentStart); |
|
return; |
|
} |
|
else if (size == -3 ) { |
|
contextHash = id; |
|
return; |
|
} |
|
else if (size == -4 ) { |
|
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))) { |
|
|
|
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 { |
|
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 if (depth > 2500 ) { |
|
takeFlatNode(start, endPos, localChildren, localPositions); |
|
} |
|
else { |
|
takeNode(start, endPos, localChildren, localPositions, localInRepeat, depth + 1); |
|
} |
|
} |
|
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 takeFlatNode(parentStart, minPos, children, positions) { |
|
let nodes = []; |
|
let nodeCount = 0, stopAt = -1; |
|
while (cursor.pos > minPos) { |
|
let { id, start, end, size } = cursor; |
|
if (size > 4) { |
|
cursor.next(); |
|
} |
|
else if (stopAt > -1 && start < stopAt) { |
|
break; |
|
} |
|
else { |
|
if (stopAt < 0) |
|
stopAt = end - maxBufferLength; |
|
nodes.push(id, start, end); |
|
nodeCount++; |
|
cursor.next(); |
|
} |
|
} |
|
if (nodeCount) { |
|
let buffer = new Uint16Array(nodeCount * 4); |
|
let start = nodes[nodes.length - 2]; |
|
for (let i = nodes.length - 3, j = 0; i >= 0; i -= 3) { |
|
buffer[j++] = nodes[i]; |
|
buffer[j++] = nodes[i + 1] - start; |
|
buffer[j++] = nodes[i + 2] - start; |
|
buffer[j++] = j; |
|
} |
|
children.push(new TreeBuffer(buffer, nodes[2] - start, nodeSet)); |
|
positions.push(start - parentStart); |
|
} |
|
} |
|
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) { |
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
|
|
if (fork.id == inRepeat && nodeSize >= 0) { |
|
|
|
|
|
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 ) |
|
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 ) { |
|
contextHash = id; |
|
} |
|
else if (size == -4 ) { |
|
lookAhead = id; |
|
} |
|
return index; |
|
} |
|
let children = [], positions = []; |
|
while (cursor.pos > 0) |
|
takeNode(data.start || 0, data.bufferStart || 0, children, positions, -1, 0); |
|
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( |
|
|
|
balanceType, |
|
|
|
children, positions, |
|
|
|
from, to, |
|
|
|
start, |
|
|
|
length, |
|
|
|
mkTop, |
|
|
|
mkTree) { |
|
let total = 0; |
|
for (let i = from; i < to; i++) |
|
total += nodeSize(balanceType, children[i]); |
|
let maxChild = Math.ceil((total * 1.5) / 8 ); |
|
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]; |
|
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); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
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(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); |
|
} |
|
|
|
|
|
|
|
get(node) { |
|
return node instanceof BufferNode ? this.getBuffer(node.context.buffer, node.index) |
|
: node instanceof TreeNode ? this.map.get(node.tree) : undefined; |
|
} |
|
|
|
|
|
|
|
cursorSet(cursor, value) { |
|
if (cursor.buffer) |
|
this.setBuffer(cursor.buffer.buffer, cursor.index, value); |
|
else |
|
this.map.set(cursor.tree, value); |
|
} |
|
|
|
|
|
|
|
|
|
cursorGet(cursor) { |
|
return cursor.buffer ? this.getBuffer(cursor.buffer.buffer, cursor.index) : this.map.get(cursor.tree); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TreeFragment { |
|
|
|
|
|
|
|
|
|
|
|
|
|
constructor( |
|
|
|
|
|
|
|
|
|
|
|
from, |
|
|
|
|
|
|
|
to, |
|
|
|
|
|
|
|
tree, |
|
|
|
|
|
|
|
|
|
|
|
|
|
offset, openStart = false, openEnd = false) { |
|
this.from = from; |
|
this.to = to; |
|
this.tree = tree; |
|
this.offset = offset; |
|
this.open = (openStart ? 1 : 0) | (openEnd ? 2 : 0); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
get openStart() { return (this.open & 1 ) > 0; } |
|
|
|
|
|
|
|
|
|
get openEnd() { return (this.open & 2 ) > 0; } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
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; |
|
} |
|
} |
|
|
|
|
|
|
|
class Parser { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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); |
|
} |
|
|
|
|
|
|
|
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); } |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function parseMixed(nest) { |
|
return (parse, input, fragments, ranges) => new MixedParse(parse, nest, input, fragments, ranges); |
|
} |
|
class InnerParse { |
|
constructor(parser, parse, overlay, target, from) { |
|
this.parser = parser; |
|
this.parse = parse; |
|
this.overlay = overlay; |
|
this.target = target; |
|
this.from = from; |
|
} |
|
} |
|
function checkRanges(ranges) { |
|
if (!ranges.length || ranges.some(r => r.from >= r.to)) |
|
throw new RangeError("Invalid inner parse ranges given: " + JSON.stringify(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++; |
|
|
|
|
|
|
|
|
|
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].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;;) { |
|
let enter = true, range; |
|
if (this.stoppedAt != null && cursor.from >= this.stoppedAt) { |
|
enter = false; |
|
} |
|
else 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 ; |
|
} |
|
else if (!cursor.type.isAnonymous && (nest = this.nest(cursor, this.input)) && |
|
(cursor.from < cursor.to || !nest.overlay)) { |
|
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 || |
|
(cursor.from < cursor.to ? [new Range(cursor.from, cursor.to)] : [])); |
|
if (ranges.length) |
|
checkRanges(ranges); |
|
if (ranges.length || !nest.overlay) |
|
this.inner.push(new InnerParse(nest.parser, ranges.length ? nest.parser.startParse(this.input, enterFragments(oldMounts, ranges), ranges) |
|
: nest.parser.startParse(""), nest.overlay ? nest.overlay.map(r => new Range(r.from - cursor.from, r.to - cursor.from)) : null, cursor.tree, ranges.length ? ranges[0].from : cursor.from)); |
|
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) { |
|
checkRanges(ranges); |
|
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[0].from)); |
|
} |
|
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 : 1 ; |
|
} |
|
return 0 ; |
|
} |
|
|
|
|
|
function sliceBuf(buf, startI, endI, nodes, positions, off) { |
|
if (startI < endI) { |
|
let from = buf.buffer[startI + 1]; |
|
nodes.push(buf.slice(startI, endI, from)); |
|
positions.push(from - off); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
function materialize(cursor) { |
|
let { node } = cursor, stack = []; |
|
let buffer = node.context.buffer; |
|
|
|
do { |
|
stack.push(cursor.index); |
|
cursor.parent(); |
|
} while (!cursor.tree); |
|
|
|
let base = cursor.tree, i = base.children.indexOf(buffer); |
|
let buf = base.children[i], b = buf.buffer, newStack = [i]; |
|
|
|
|
|
function split(startI, endI, type, innerOffset, length, stackPos) { |
|
let targetI = stack[stackPos]; |
|
let children = [], positions = []; |
|
sliceBuf(buf, startI, targetI, children, positions, innerOffset); |
|
let from = b[targetI + 1], to = b[targetI + 2]; |
|
newStack.push(children.length); |
|
let child = stackPos |
|
? split(targetI + 4, b[targetI + 3], buf.set.types[b[targetI]], from, to - from, stackPos - 1) |
|
: node.toTree(); |
|
children.push(child); |
|
positions.push(from - innerOffset); |
|
sliceBuf(buf, b[targetI + 3], endI, children, positions, innerOffset); |
|
return new Tree(type, children, positions, length); |
|
} |
|
base.children[i] = split(0, b.length, NodeType.none, 0, buf.length, stack.length - 1); |
|
|
|
for (let index of newStack) { |
|
let tree = cursor.tree.children[index], pos = cursor.tree.positions[index]; |
|
cursor.yield(new TreeNode(tree, pos + cursor.from, index, cursor._tree)); |
|
} |
|
} |
|
class StructureCursor { |
|
constructor(root, offset) { |
|
this.offset = offset; |
|
this.done = false; |
|
this.cursor = root.cursor(IterMode.IncludeAnonymous | IterMode.IgnoreMounts); |
|
} |
|
|
|
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; |
|
} |
|
|
|
|
|
|
|
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.openStart, frag.to <= end || frag.openEnd)); |
|
if (last) |
|
break; |
|
pos = changes[i].to; |
|
} |
|
} |
|
else { |
|
result.push(new TreeFragment(from, to, mount.tree, -startPos, frag.from >= startPos || frag.openStart, frag.to <= endPos || frag.openEnd)); |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
export { DefaultBufferLength, IterMode, MountedTree, NodeProp, NodeSet, NodeType, NodeWeakMap, Parser, Tree, TreeBuffer, TreeCursor, TreeFragment, parseMixed }; |
|
|