lezer-tree
Advanced tools
Comparing version 0.11.1 to 0.12.0
@@ -0,1 +1,27 @@ | ||
## 0.12.0 (2020-10-23) | ||
### Breaking changes | ||
`Tree.iterate` no longer allows returning from inside the iteration (use cursors directly for that kind of use cases). | ||
`Subtree` has been renamed to `SyntaxNode` and narrowed in scope a little. | ||
The `top`, `skipped`, and `error` node props no longer exist. | ||
### New features | ||
The package now offers a `TreeCursor` abstraction, which can be used for both regular iteration and for custom traversal of a tree. | ||
`SyntaxNode` instances have `nextSibling`/`prevSibling` getters that allow more direct navigation through the tree. | ||
Node types now expose `isTop`, `isSkipped`, `isError`, and `isRepeated` properties that indicate special status. | ||
Adds `NodeProp.group` to assign group names to node types. | ||
Syntax nodes now have helper functions `getChild` and `getChildren` to retrieve direct child nodes by type or group. | ||
`NodeType.match` (and thus `NodeProp.add`) now allows types to be targeted by group name. | ||
Node types have a new `is` method for checking whether their name or one of their groups matches a given string. | ||
## 0.11.1 (2020-09-26) | ||
@@ -2,0 +28,0 @@ |
@@ -8,10 +8,2 @@ export declare const DefaultBufferLength = 1024; | ||
} | ||
declare type EnterFunc<T> = (type: NodeType, start: number, end: number) => T | false | undefined; | ||
declare type LeaveFunc = (type: NodeType, start: number, end: number) => void; | ||
declare type IterateArgs<T> = { | ||
enter: EnterFunc<T>; | ||
leave?: LeaveFunc; | ||
from?: number; | ||
to?: number; | ||
}; | ||
export declare class NodeProp<T> { | ||
@@ -33,7 +25,5 @@ deserialize: (str: string) => T; | ||
} | ((type: NodeType) => T | undefined)): NodePropSource; | ||
static error: NodeProp<boolean>; | ||
static skipped: NodeProp<boolean>; | ||
static closedBy: NodeProp<readonly string[]>; | ||
static openedBy: NodeProp<readonly string[]>; | ||
static top: NodeProp<boolean>; | ||
static group: NodeProp<readonly string[]>; | ||
} | ||
@@ -46,2 +36,7 @@ export declare class NodePropSource { | ||
prop<T>(prop: NodeProp<T>): T | undefined; | ||
get isTop(): boolean; | ||
get isSkipped(): boolean; | ||
get isError(): boolean; | ||
get isRepeated(): boolean; | ||
is(name: string): boolean; | ||
static none: NodeType; | ||
@@ -57,18 +52,3 @@ static match<T>(map: { | ||
} | ||
export declare abstract class Subtree { | ||
abstract parent: Subtree | null; | ||
abstract type: NodeType; | ||
get name(): string; | ||
abstract start: number; | ||
abstract end: number; | ||
get depth(): number; | ||
get root(): Tree; | ||
abstract iterate<T = any>(args: IterateArgs<T>): T | undefined; | ||
resolve(pos: number, side?: -1 | 0 | 1): Subtree; | ||
abstract childBefore(pos: number): Subtree | null; | ||
abstract childAfter(pos: number): Subtree | null; | ||
get firstChild(): Subtree | null; | ||
get lastChild(): Subtree | null; | ||
} | ||
export declare class Tree extends Subtree { | ||
export declare class Tree { | ||
readonly type: NodeType; | ||
@@ -78,6 +58,3 @@ readonly children: readonly (Tree | TreeBuffer)[]; | ||
readonly length: number; | ||
parent: null; | ||
constructor(type: NodeType, children: readonly (Tree | TreeBuffer)[], positions: readonly number[], length: number); | ||
get start(): number; | ||
get end(): number; | ||
private partial; | ||
@@ -87,5 +64,11 @@ applyChanges(changes: readonly ChangedRange[]): Tree; | ||
static empty: Tree; | ||
iterate<T = any>({ from, to, enter, leave }: IterateArgs<T>): T | undefined; | ||
childBefore(pos: number): Subtree | null; | ||
childAfter(pos: number): Subtree | null; | ||
cursor(pos?: number, side?: -1 | 0 | 1): TreeCursor; | ||
get topNode(): SyntaxNode; | ||
resolve(pos: number, side?: -1 | 0 | 1): SyntaxNode; | ||
iterate(spec: { | ||
enter(type: NodeType, from: number, to: number): false | void; | ||
leave?(type: NodeType, from: number, to: number): void; | ||
from?: number; | ||
to?: number; | ||
}): void; | ||
append(other: Tree): Tree; | ||
@@ -95,3 +78,3 @@ balance(maxBufferLength?: number): Tree; | ||
} | ||
export declare type BuildData = { | ||
declare type BuildData = { | ||
buffer: BufferCursor | readonly number[]; | ||
@@ -107,5 +90,46 @@ group: NodeGroup; | ||
readonly type: NodeType; | ||
iterate<T = any>({ from, to, enter, leave }: IterateArgs<T>): T | undefined; | ||
private parentNodesByEnd; | ||
} | ||
export interface SyntaxNode { | ||
type: NodeType; | ||
name: string; | ||
from: number; | ||
to: number; | ||
parent: SyntaxNode | null; | ||
firstChild: SyntaxNode | null; | ||
lastChild: SyntaxNode | null; | ||
childAfter(pos: number): SyntaxNode | null; | ||
childBefore(pos: number): SyntaxNode | null; | ||
nextSibling: SyntaxNode | null; | ||
prevSibling: SyntaxNode | null; | ||
cursor: TreeCursor; | ||
resolve(pos: number, side?: -1 | 0 | 1): SyntaxNode; | ||
getChild(type: string, before?: string | null, after?: string | null): SyntaxNode | null; | ||
getChildren(type: string, before?: string | null, after?: string | null): SyntaxNode[]; | ||
} | ||
export declare class TreeCursor { | ||
type: NodeType; | ||
get name(): string; | ||
from: number; | ||
to: number; | ||
private buffer; | ||
private stack; | ||
private index; | ||
private bufferNode; | ||
private yieldNode; | ||
private yieldBuf; | ||
private yield; | ||
firstChild(): boolean; | ||
lastChild(): boolean; | ||
childAfter(pos: number): boolean; | ||
childBefore(pos: number): boolean; | ||
parent(): boolean; | ||
nextSibling(): boolean; | ||
prevSibling(): boolean; | ||
private atLastNode; | ||
private move; | ||
next(): boolean; | ||
prev(): boolean; | ||
moveTo(pos: number, side?: -1 | 0 | 1): this; | ||
get node(): SyntaxNode; | ||
} | ||
export interface BufferCursor { | ||
@@ -112,0 +136,0 @@ pos: number; |
1265
dist/tree.es.js
/// The default maximum length of a `TreeBuffer` node. | ||
export const DefaultBufferLength = 1024; | ||
class Iteration { | ||
constructor(enter, leave) { | ||
this.enter = enter; | ||
this.leave = leave; | ||
this.result = undefined; | ||
} | ||
get done() { return this.result !== undefined; } | ||
doEnter(type, start, end) { | ||
let value = this.enter(type, start, end); | ||
if (value === undefined) | ||
return true; | ||
if (value !== false) | ||
this.result = value; | ||
return false; | ||
} | ||
} | ||
let nextPropID = 0; | ||
var DefaultBufferLength = 1024; | ||
var nextPropID = 0; | ||
var CachedNode = new WeakMap(); | ||
/// Each [node type](#tree.NodeType) can have metadata associated with | ||
/// it in props. Instances of this class represent prop names. | ||
export class NodeProp { | ||
var NodeProp = /** @class */ (function () { | ||
/// Create a new node prop type. You can optionally pass a | ||
/// `deserialize` function. | ||
constructor({ deserialize } = {}) { | ||
function NodeProp(_a) { | ||
var deserialize = (_a === void 0 ? {} : _a).deserialize; | ||
this.id = nextPropID++; | ||
this.deserialize = deserialize || (() => { | ||
this.deserialize = deserialize || (function () { | ||
throw new Error("This node type doesn't define a deserialize function"); | ||
@@ -33,9 +19,9 @@ }); | ||
/// the identity function. | ||
static string() { return new NodeProp({ deserialize: str => str }); } | ||
NodeProp.string = function () { return new NodeProp({ deserialize: function (str) { return str; } }); }; | ||
/// Create a number-valued node prop whose deserialize function is | ||
/// just `Number`. | ||
static number() { return new NodeProp({ deserialize: Number }); } | ||
NodeProp.number = function () { return new NodeProp({ deserialize: Number }); }; | ||
/// Creates a boolean-valued node prop whose deserialize function | ||
/// returns true for any input. | ||
static flag() { return new NodeProp({ deserialize: () => true }); } | ||
NodeProp.flag = function () { return new NodeProp({ deserialize: function () { return true; } }); }; | ||
/// Store a value for this prop in the given object. This can be | ||
@@ -45,6 +31,6 @@ /// useful when building up a prop object to pass to the | ||
/// argument. | ||
set(propObj, value) { | ||
NodeProp.prototype.set = function (propObj, value) { | ||
propObj[this.id] = value; | ||
return propObj; | ||
} | ||
}; | ||
/// This is meant to be used with | ||
@@ -57,29 +43,25 @@ /// [`NodeGroup.extend`](#tree.NodeGroup.extend) or | ||
/// it does. | ||
add(match) { | ||
NodeProp.prototype.add = function (match) { | ||
return new NodePropSource(this, typeof match == "function" ? match : NodeType.match(match)); | ||
} | ||
} | ||
/// The special node type that the parser uses to represent parse | ||
/// errors has this flag set. (You shouldn't use it for custom nodes | ||
/// that represent erroneous content.) | ||
NodeProp.error = NodeProp.flag(); | ||
/// Nodes that were produced by skipped expressions (such as | ||
/// comments) have this prop set to true. | ||
NodeProp.skipped = NodeProp.flag(); | ||
/// 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 [`openedBy`](#tree.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(" ") }); | ||
/// Indicates that this node indicates a top level document. | ||
NodeProp.top = NodeProp.flag(); | ||
}; | ||
/// 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: function (str) { return str.split(" "); } }); | ||
/// The inverse of [`openedBy`](#tree.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: function (str) { return 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: function (str) { return str.split(" "); } }); | ||
return NodeProp; | ||
}()); | ||
/// Type returned by [`NodeProp.add`](#tree.NodeProp.add). Describes | ||
/// the way a prop should be added to each node type in a node group. | ||
export class NodePropSource { | ||
var NodePropSource = /** @class */ (function () { | ||
/// @internal | ||
constructor( | ||
function NodePropSource( | ||
/// @internal | ||
@@ -92,7 +74,8 @@ prop, | ||
} | ||
} | ||
return NodePropSource; | ||
}()); | ||
/// Each node in a syntax tree has a node type associated with it. | ||
export class NodeType { | ||
var NodeType = /** @class */ (function () { | ||
/// @internal | ||
constructor( | ||
function NodeType( | ||
/// The name of the node type. Not necessarily unique, but if the | ||
@@ -107,25 +90,72 @@ /// grammar was written properly, different node types with the | ||
/// used in the parser. | ||
id) { | ||
id, | ||
/// @internal | ||
flags) { | ||
if (flags === void 0) { flags = 0; } | ||
this.name = name; | ||
this.props = props; | ||
this.id = id; | ||
this.flags = flags; | ||
} | ||
/// 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]; } | ||
NodeType.prototype.prop = function (prop) { return this.props[prop.id]; }; | ||
Object.defineProperty(NodeType.prototype, "isTop", { | ||
/// True when this is the top node of a grammar. | ||
get: function () { return (this.flags & 1) > 0; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(NodeType.prototype, "isSkipped", { | ||
/// True when this node is produced by a skip rule. | ||
get: function () { return (this.flags & 2) > 0; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(NodeType.prototype, "isError", { | ||
/// Indicates whether this is an error node. | ||
get: function () { return (this.flags & 4) > 0; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(NodeType.prototype, "isRepeated", { | ||
/// When true, this node type is used to cache repetition, and is | ||
/// not a user-defined named node. | ||
get: function () { return (this.flags & 8) > 0; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
/// Returns true when this node's name or one of its | ||
/// [groups](#tree.NodeProp^group) matches the given string. | ||
NodeType.prototype.is = function (name) { | ||
if (this.name == name) | ||
return true; | ||
var group = this.prop(NodeProp.group); | ||
return group ? group.indexOf(name) > -1 : false; | ||
}; | ||
/// Create a function from node types to arbitrary values by | ||
/// specifying an object whose property names are node names. Often | ||
/// useful with [`NodeProp.add`](#tree.NodeProp.add). You can put | ||
/// multiple node 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(" ")) | ||
/// specifying an object whose property names are node or | ||
/// [group](#tree.NodeProp^group) names. Often useful with | ||
/// [`NodeProp.add`](#tree.NodeProp.add). You can put multiple | ||
/// names, separated by spaces, in a single property name to map | ||
/// multiple node names to a single value. | ||
NodeType.match = function (map) { | ||
var direct = Object.create(null); | ||
for (var prop in map) | ||
for (var _i = 0, _a = prop.split(" "); _i < _a.length; _i++) { | ||
var name = _a[_i]; | ||
direct[name] = map[prop]; | ||
return (node) => direct[node.name]; | ||
} | ||
} | ||
/// An empty dummy node type to use when no actual type is available. | ||
NodeType.none = new NodeType("", Object.create(null), 0); | ||
} | ||
return function (node) { | ||
for (var groups = node.prop(NodeProp.group), i = -1; i < (groups ? groups.length : 0); i++) { | ||
var 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); | ||
return NodeType; | ||
}()); | ||
/// A node group holds a collection of node types. It is used to | ||
@@ -139,10 +169,10 @@ /// compactly represent trees by storing their type ids, rather than a | ||
/// slots. | ||
export class NodeGroup { | ||
var NodeGroup = /** @class */ (function () { | ||
/// Create a group with the given types. The `id` property of each | ||
/// type should correspond to its position within the array. | ||
constructor( | ||
function NodeGroup( | ||
/// The node types in this group, by id. | ||
types) { | ||
this.types = types; | ||
for (let i = 0; i < types.length; i++) | ||
for (var i = 0; i < types.length; i++) | ||
if (types[i].id != i) | ||
@@ -154,12 +184,18 @@ throw new RangeError("Node type ids should correspond to array positions when creating a node group"); | ||
/// [`NodeProp.add`](#tree.NodeProp.add). | ||
extend(...props) { | ||
let newTypes = []; | ||
for (let type of this.types) { | ||
let newProps = null; | ||
for (let source of props) { | ||
let value = source.f(type); | ||
NodeGroup.prototype.extend = function () { | ||
var props = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
props[_i] = arguments[_i]; | ||
} | ||
var newTypes = []; | ||
for (var _a = 0, _b = this.types; _a < _b.length; _a++) { | ||
var type = _b[_a]; | ||
var newProps = null; | ||
for (var _c = 0, props_1 = props; _c < props_1.length; _c++) { | ||
var source = props_1[_c]; | ||
var value = source.f(type); | ||
if (value !== undefined) { | ||
if (!newProps) { | ||
newProps = Object.create(null); | ||
for (let prop in type.props) | ||
for (var prop in type.props) | ||
newProps[prop] = type.props[prop]; | ||
@@ -170,51 +206,8 @@ } | ||
} | ||
newTypes.push(newProps ? new NodeType(type.name, newProps, type.id) : type); | ||
newTypes.push(newProps ? new NodeType(type.name, newProps, type.id, type.flags) : type); | ||
} | ||
return new NodeGroup(newTypes); | ||
} | ||
} | ||
/// A subtree is a representation of part of the syntax tree. It may | ||
/// either be the tree root, or a tagged node. | ||
export class Subtree { | ||
// Shorthand for `.type.name`. | ||
get name() { return this.type.name; } | ||
/// The depth (number of parent nodes) of this subtree | ||
get depth() { | ||
let d = 0; | ||
for (let p = this.parent; p; p = p.parent) | ||
d++; | ||
return d; | ||
} | ||
/// The root of the tree that this subtree is part of | ||
get root() { | ||
let cx = this; | ||
while (cx.parent) | ||
cx = cx.parent; | ||
return cx; | ||
} | ||
/// Find the node at a given position. By default, this will return | ||
/// the lowest-depth subtree that covers the position from both | ||
/// sides, meaning that nodes starting or ending at the position | ||
/// aren't entered. You can pass a `side` of `-1` to enter nodes | ||
/// that end at the position, or `1` to enter nodes that start | ||
/// there. | ||
resolve(pos, side = 0) { | ||
let result = this.resolveAt(pos); | ||
// FIXME this is slightly inefficient in that it scans the result | ||
// of resolveAt twice (but further complicating child-finding | ||
// logic seems unattractive as well) | ||
if (side != 0) | ||
for (;;) { | ||
let child = (side < 0 ? result.childBefore(pos) : result.childAfter(pos)); | ||
if (!child || (side < 0 ? child.end : child.start) != pos) | ||
break; | ||
result = child; | ||
} | ||
return result; | ||
} | ||
/// Get the first child of this subtree. | ||
get firstChild() { return this.childAfter(this.start - 1); } | ||
/// Find the last child of this subtree. | ||
get lastChild() { return this.childBefore(this.end + 1); } | ||
} | ||
}; | ||
return NodeGroup; | ||
}()); | ||
/// A piece of syntax tree. There are two ways to approach these | ||
@@ -230,11 +223,11 @@ /// trees: the way they are actually stored in memory, and the | ||
/// representation is very awkward, so most client code will want to | ||
/// use the `Subtree` interface instead, which provides a view on some | ||
/// part of this data structure, and can be used (through `resolve`, | ||
/// for example) to zoom in on any single node. | ||
export class Tree extends Subtree { | ||
/// use the `TreeCursor` interface instead, which provides a view on | ||
/// some part of this data structure, and can be used to move around | ||
/// to adjacent nodes. | ||
var Tree = /** @class */ (function () { | ||
/// Construct a new tree. You usually want to go through | ||
/// [`Tree.build`](#tree.Tree^build) instead. | ||
constructor(type, | ||
function Tree(type, | ||
/// The tree's child nodes. Children small enough to fit in a | ||
/// `TreeBuffer` will be represented as such, other children can be | ||
/// `TreeBuffer will be represented as such, other children can be | ||
/// further `Tree` instances with their own internal structure. | ||
@@ -247,3 +240,2 @@ children, | ||
length) { | ||
super(); | ||
this.type = type; | ||
@@ -254,17 +246,15 @@ this.children = children; | ||
} | ||
get start() { return 0; } | ||
get end() { return this.length; } | ||
/// @internal | ||
toString() { | ||
let children = this.children.map(c => c.toString()).join(); | ||
return !this.name ? children : | ||
(/\W/.test(this.name) && !this.type.prop(NodeProp.error) ? JSON.stringify(this.name) : this.name) + | ||
Tree.prototype.toString = function () { | ||
var children = this.children.map(function (c) { return c.toString(); }).join(); | ||
return !this.type.name ? children : | ||
(/\W/.test(this.type.name) && !this.type.isError ? JSON.stringify(this.type.name) : this.type.name) + | ||
(children.length ? "(" + children + ")" : ""); | ||
} | ||
partial(start, end, offset, children, positions) { | ||
for (let i = 0; i < this.children.length; i++) { | ||
let from = this.positions[i]; | ||
}; | ||
Tree.prototype.partial = function (start, end, offset, children, positions) { | ||
for (var i = 0; i < this.children.length; i++) { | ||
var from = this.positions[i]; | ||
if (from > end) | ||
break; | ||
let child = this.children[i], to = from + child.length; | ||
var child = this.children[i], to = from + child.length; | ||
if (to < start) | ||
@@ -280,3 +270,3 @@ continue; | ||
} | ||
} | ||
}; | ||
/// Apply a set of edits to a tree, removing all nodes that were | ||
@@ -288,23 +278,24 @@ /// touched by the edits, and moving remaining nodes so that their | ||
/// subsequent incremental re-parse. | ||
applyChanges(changes) { | ||
Tree.prototype.applyChanges = function (changes) { | ||
if (changes.length == 0) | ||
return this; | ||
let children = [], positions = []; | ||
var children = [], positions = []; | ||
function cutAt(tree, pos, side) { | ||
let found = -1; | ||
tree.iterate({ | ||
from: pos, | ||
to: side < 0 ? 0 : tree.length, | ||
enter() { return found < 0 ? undefined : false; }, | ||
leave(type, start, end) { | ||
if (found < 0 && (side < 0 ? end <= pos : start >= pos) && !type.prop(NodeProp.error)) | ||
found = side < 0 ? Math.min(pos, end - 1) : Math.max(pos, start + 1); | ||
} | ||
}); | ||
return found > -1 ? found : side < 0 ? 0 : tree.length; | ||
var cursor = tree.cursor(pos, -side); | ||
for (;;) { | ||
if (!cursor.enter(side, pos)) | ||
for (;;) { | ||
if ((side < 0 ? cursor.to <= pos : cursor.from >= pos) && !cursor.type.isError) | ||
return side < 0 ? Math.min(pos, cursor.to - 1) : Math.max(pos, cursor.from + 1); | ||
if (cursor.sibling(side)) | ||
break; | ||
if (!cursor.parent()) | ||
return side < 0 ? 0 : tree.length; | ||
} | ||
} | ||
} | ||
let off = 0; | ||
for (let i = 0, pos = 0;; i++) { | ||
let next = i == changes.length ? null : changes[i]; | ||
let nextPos = next ? cutAt(this, next.fromA, -1) : this.length; | ||
var off = 0; | ||
for (var i = 0, pos = 0;; i++) { | ||
var next = i == changes.length ? null : changes[i]; | ||
var nextPos = next ? cutAt(this, next.fromA, -1) : this.length; | ||
if (nextPos > pos) | ||
@@ -318,13 +309,13 @@ this.partial(pos, nextPos, off, children, positions); | ||
return new Tree(NodeType.none, children, positions, this.length + off); | ||
} | ||
}; | ||
/// Take the part of the tree up to the given position. | ||
cut(at) { | ||
Tree.prototype.cut = function (at) { | ||
if (at >= this.length) | ||
return this; | ||
let children = [], positions = []; | ||
for (let i = 0; i < this.children.length; i++) { | ||
let from = this.positions[i]; | ||
var children = [], positions = []; | ||
for (var i = 0; i < this.children.length; i++) { | ||
var from = this.positions[i]; | ||
if (from >= at) | ||
break; | ||
let child = this.children[i], to = from + child.length; | ||
var child = this.children[i], to = from + child.length; | ||
children.push(to <= at ? child : child.cut(at - from)); | ||
@@ -334,118 +325,79 @@ positions.push(from); | ||
return new Tree(this.type, children, positions, at); | ||
} | ||
iterate({ from = this.start, to = this.end, enter, leave }) { | ||
let iter = new Iteration(enter, leave); | ||
this.iterInner(from, to, 0, iter); | ||
return iter.result; | ||
} | ||
/// @internal | ||
iterInner(from, to, offset, iter) { | ||
if (this.type.name && !iter.doEnter(this.type, offset, offset + this.length)) | ||
return; | ||
if (from <= to) { | ||
for (let i = 0; i < this.children.length && !iter.done; i++) { | ||
let child = this.children[i], start = this.positions[i] + offset, end = start + child.length; | ||
if (start > to) | ||
break; | ||
if (end < from) | ||
continue; | ||
child.iterInner(from, to, start, iter); | ||
} | ||
}; | ||
/// Get a [tree cursor](#tree.TreeCursor) rooted at this tree. When | ||
/// `pos` is given, the cursor is [moved](#tree.TreeCursor.moveTo) | ||
/// to the given position and side. | ||
Tree.prototype.cursor = function (pos, side) { | ||
if (side === void 0) { side = 0; } | ||
var scope = (pos != null && CachedNode.get(this)) || this.topNode; | ||
var cursor = new TreeCursor(scope); | ||
if (pos != null) { | ||
cursor.moveTo(pos, side); | ||
CachedNode.set(this, cursor.tree); | ||
} | ||
else { | ||
for (let i = this.children.length - 1; i >= 0 && !iter.done; i--) { | ||
let child = this.children[i], start = this.positions[i] + offset, end = start + child.length; | ||
if (end < to) | ||
break; | ||
if (start > from) | ||
return cursor; | ||
}; | ||
Object.defineProperty(Tree.prototype, "topNode", { | ||
/// Get a [syntax node](#tree.SyntaxNode) object for the top of the | ||
/// tree. | ||
get: function () { | ||
return new TreeNode(this, 0, 0, null); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
/// Get the [syntax node](#tree.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. | ||
Tree.prototype.resolve = function (pos, side) { | ||
if (side === void 0) { side = 0; } | ||
return this.cursor(pos, side).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`, the given node | ||
/// will not have its children iterated over (or `leave` called). | ||
Tree.prototype.iterate = function (spec) { | ||
var enter = spec.enter, leave = spec.leave, _a = spec.from, from = _a === void 0 ? 0 : _a, _b = spec.to, to = _b === void 0 ? this.length : _b; | ||
for (var c = this.cursor();;) { | ||
var mustLeave = false; | ||
if (c.from <= to && c.to >= from && (c.type.isRepeated || enter(c.type, c.from, c.to) !== false)) { | ||
if (c.firstChild()) | ||
continue; | ||
child.iterInner(from, to, start, iter); | ||
mustLeave = true; | ||
} | ||
} | ||
if (iter.leave && this.type.name) | ||
iter.leave(this.type, offset, offset + this.length); | ||
return; | ||
} | ||
/// @internal | ||
resolveAt(pos) { | ||
if (cacheRoot == this) { | ||
for (let tree = cached;;) { | ||
let next = tree.parent; | ||
if (!next) | ||
for (;;) { | ||
if (mustLeave && leave) | ||
leave(c.type, c.from, c.to); | ||
if (c.nextSibling()) | ||
break; | ||
if (tree.start < pos && tree.end > pos) | ||
return tree.resolve(pos); | ||
tree = next; | ||
if (!c.parent()) | ||
return; | ||
mustLeave = true; | ||
} | ||
} | ||
cacheRoot = this; | ||
return cached = this.resolveInner(pos, 0, this); | ||
} | ||
childBefore(pos) { | ||
return this.findChild(pos, -1, 0, this); | ||
} | ||
childAfter(pos) { | ||
return this.findChild(pos, 1, 0, this); | ||
} | ||
/// @internal | ||
findChild(pos, side, start, parent) { | ||
for (let i = 0; i < this.children.length; i++) { | ||
let childStart = this.positions[i] + start, select = -1; | ||
if (childStart >= pos) { | ||
if (side < 0 && i > 0) | ||
select = i - 1; | ||
else if (side > 0) | ||
select = i; | ||
else | ||
break; | ||
} | ||
if (select < 0 && (childStart + this.children[i].length > pos || side < 0 && i == this.children.length - 1)) | ||
select = i; | ||
if (select >= 0) { | ||
let child = this.children[select], childStart = this.positions[select] + start; | ||
if (child.length == 0 && childStart == pos) | ||
continue; | ||
if (child instanceof Tree) { | ||
if (child.type.name) | ||
return new NodeSubtree(child, childStart, parent); | ||
return child.findChild(pos, side, childStart, parent); | ||
} | ||
else { | ||
let found = child.findIndex(pos, side, childStart, 0, child.buffer.length); | ||
if (found > -1) | ||
return new BufferSubtree(child, childStart, found, parent); | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
/// @internal | ||
resolveInner(pos, start, parent) { | ||
let found = this.findChild(pos, 0, start, parent); | ||
return found ? found.resolveAt(pos) : parent; | ||
} | ||
}; | ||
/// Append another tree to this tree. `other` must have empty space | ||
/// big enough to fit this tree at its start. | ||
append(other) { | ||
Tree.prototype.append = function (other) { | ||
if (other.children.length && other.positions[0] < this.length) | ||
throw new Error("Can't append overlapping trees"); | ||
return new Tree(this.type, this.children.concat(other.children), this.positions.concat(other.positions), other.length); | ||
} | ||
}; | ||
/// Balance the direct children of this tree. | ||
balance(maxBufferLength = DefaultBufferLength) { | ||
Tree.prototype.balance = function (maxBufferLength) { | ||
if (maxBufferLength === void 0) { maxBufferLength = DefaultBufferLength; } | ||
return this.children.length <= BalanceBranchFactor ? this | ||
: balanceRange(this.type, NodeType.none, this.children, this.positions, 0, this.children.length, 0, maxBufferLength, this.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); | ||
Tree.prototype.parent = null; | ||
// Top-level `resolveAt` calls store their last result here, so that | ||
// if the next call is near the last, parent trees can be cheaply | ||
// reused. | ||
let cacheRoot = Tree.empty; | ||
let cached = Tree.empty; | ||
/// or a cursor over such a buffer. | ||
Tree.build = function (data) { return buildTree(data); }; | ||
/// The empty tree | ||
Tree.empty = new Tree(NodeType.none, [], [], 0); | ||
return Tree; | ||
}()); | ||
/// Tree buffers contain (type, start, end, endIndex) quads for each | ||
@@ -455,5 +407,5 @@ /// node. In such a buffer, nodes are stored in prefix order (parents | ||
/// children belong to it) | ||
export class TreeBuffer { | ||
var TreeBuffer = /** @class */ (function () { | ||
/// Create a tree buffer @internal | ||
constructor( | ||
function TreeBuffer( | ||
/// @internal | ||
@@ -464,3 +416,4 @@ buffer, | ||
/// @internal | ||
group, type = NodeType.none) { | ||
group, type) { | ||
if (type === void 0) { type = NodeType.none; } | ||
this.buffer = buffer; | ||
@@ -472,31 +425,33 @@ this.length = length; | ||
/// @internal | ||
toString() { | ||
let parts = []; | ||
for (let index = 0; index < this.buffer.length;) | ||
index = this.childToString(index, parts); | ||
return parts.join(","); | ||
} | ||
TreeBuffer.prototype.toString = function () { | ||
var result = []; | ||
for (var index = 0; index < this.buffer.length;) { | ||
result.push(this.childString(index)); | ||
index = this.buffer[index + 3]; | ||
} | ||
return result.join(","); | ||
}; | ||
/// @internal | ||
childToString(index, parts) { | ||
let id = this.buffer[index], endIndex = this.buffer[index + 3]; | ||
let type = this.group.types[id], result = type.name; | ||
if (/\W/.test(result) && !type.prop(NodeProp.error)) | ||
TreeBuffer.prototype.childString = function (index) { | ||
var id = this.buffer[index], endIndex = this.buffer[index + 3]; | ||
var type = this.group.types[id], result = type.name; | ||
if (/\W/.test(result) && !type.isError) | ||
result = JSON.stringify(result); | ||
index += 4; | ||
if (endIndex > index) { | ||
let children = []; | ||
while (index < endIndex) | ||
index = this.childToString(index, children); | ||
result += "(" + children.join(",") + ")"; | ||
if (endIndex == index) | ||
return result; | ||
var children = []; | ||
while (index < endIndex) { | ||
children.push(this.childString(index)); | ||
index = this.buffer[index + 3]; | ||
} | ||
parts.push(result); | ||
return index; | ||
} | ||
return result + "(" + children.join(",") + ")"; | ||
}; | ||
/// @internal | ||
cut(at) { | ||
let cutPoint = 0; | ||
TreeBuffer.prototype.cut = function (at) { | ||
var cutPoint = 0; | ||
while (cutPoint < this.buffer.length && this.buffer[cutPoint + 1] < at) | ||
cutPoint += 4; | ||
let newBuffer = new Uint16Array(cutPoint); | ||
for (let i = 0; i < cutPoint; i += 4) { | ||
var newBuffer = new Uint16Array(cutPoint); | ||
for (var i = 0; i < cutPoint; i += 4) { | ||
newBuffer[i] = this.buffer[i]; | ||
@@ -508,198 +463,519 @@ newBuffer[i + 1] = this.buffer[i + 1]; | ||
return new TreeBuffer(newBuffer, Math.min(at, this.length), this.group); | ||
}; | ||
/// @internal | ||
TreeBuffer.prototype.findChild = function (startIndex, endIndex, dir, after) { | ||
var buffer = this.buffer, pick = -1; | ||
for (var i = startIndex; i != endIndex; i = buffer[i + 3]) { | ||
if (after != -100000000 /* None */) { | ||
var start = buffer[i + 1], end = buffer[i + 2]; | ||
if (dir > 0) { | ||
if (end > after) | ||
pick = i; | ||
if (end > after) | ||
break; | ||
} | ||
else { | ||
if (start < after) | ||
pick = i; | ||
if (end >= after) | ||
break; | ||
} | ||
} | ||
else { | ||
pick = i; | ||
if (dir > 0) | ||
break; | ||
} | ||
} | ||
return pick; | ||
}; | ||
return TreeBuffer; | ||
}()); | ||
var TreeNode = /** @class */ (function () { | ||
function TreeNode(node, from, index, _parent) { | ||
this.node = node; | ||
this.from = from; | ||
this.index = index; | ||
this._parent = _parent; | ||
} | ||
iterate({ from = 0, to = this.length, enter, leave }) { | ||
let iter = new Iteration(enter, leave); | ||
this.iterInner(from, to, 0, iter); | ||
return iter.result; | ||
Object.defineProperty(TreeNode.prototype, "type", { | ||
get: function () { return this.node.type; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(TreeNode.prototype, "name", { | ||
get: function () { return this.node.type.name; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(TreeNode.prototype, "to", { | ||
get: function () { return this.from + this.node.length; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
TreeNode.prototype.nextChild = function (i, dir, after) { | ||
for (var parent = this;;) { | ||
for (var _a = parent.node, children = _a.children, positions = _a.positions, e = dir > 0 ? children.length : -1; i != e; i += dir) { | ||
var next = children[i], start = positions[i] + parent.from; | ||
if (after != -100000000 /* None */ && (dir < 0 ? start >= after : start + next.length <= after)) | ||
continue; | ||
if (next instanceof TreeBuffer) { | ||
var index = next.findChild(0, next.buffer.length, dir, after == -100000000 /* None */ ? -100000000 /* None */ : after - start); | ||
if (index > -1) | ||
return new BufferNode(new BufferContext(parent, next, i, start), null, index); | ||
} | ||
else if (!next.type.isRepeated || hasChild(next)) { | ||
var inner = new TreeNode(next, start, i, parent); | ||
return !inner.type.isRepeated ? inner : inner.nextChild(dir < 0 ? next.children.length - 1 : 0, dir, after); | ||
} | ||
} | ||
if (!parent.type.isRepeated) | ||
return null; | ||
i = parent.index + dir; | ||
parent = parent._parent; | ||
} | ||
}; | ||
Object.defineProperty(TreeNode.prototype, "firstChild", { | ||
get: function () { return this.nextChild(0, 1, -100000000 /* None */); }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(TreeNode.prototype, "lastChild", { | ||
get: function () { return this.nextChild(this.node.children.length - 1, -1, -100000000 /* None */); }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
TreeNode.prototype.childAfter = function (pos) { return this.nextChild(0, 1, pos); }; | ||
TreeNode.prototype.childBefore = function (pos) { return this.nextChild(this.node.children.length - 1, -1, pos); }; | ||
TreeNode.prototype.nextSignificant = function () { | ||
var val = this; | ||
while (val.type.isRepeated) | ||
val = val._parent; | ||
return val; | ||
}; | ||
Object.defineProperty(TreeNode.prototype, "parent", { | ||
get: function () { | ||
return this._parent ? this._parent.nextSignificant() : null; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(TreeNode.prototype, "nextSibling", { | ||
get: function () { | ||
return this._parent ? this._parent.nextChild(this.index + 1, 1, -1) : null; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(TreeNode.prototype, "prevSibling", { | ||
get: function () { | ||
return this._parent ? this._parent.nextChild(this.index - 1, -1, -1) : null; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(TreeNode.prototype, "cursor", { | ||
get: function () { return new TreeCursor(this); }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
TreeNode.prototype.resolve = function (pos, side) { | ||
if (side === void 0) { side = 0; } | ||
return this.cursor.moveTo(pos, side).node; | ||
}; | ||
TreeNode.prototype.getChild = function (type, before, after) { | ||
if (before === void 0) { before = null; } | ||
if (after === void 0) { after = null; } | ||
var r = getChildren(this, type, before, after); | ||
return r.length ? r[0] : null; | ||
}; | ||
TreeNode.prototype.getChildren = function (type, before, after) { | ||
if (before === void 0) { before = null; } | ||
if (after === void 0) { after = null; } | ||
return getChildren(this, type, before, after); | ||
}; | ||
/// @internal | ||
TreeNode.prototype.toString = function () { return this.node.toString(); }; | ||
return TreeNode; | ||
}()); | ||
function getChildren(node, type, before, after) { | ||
var 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 : []; | ||
} | ||
} | ||
var BufferContext = /** @class */ (function () { | ||
function BufferContext(parent, buffer, index, start) { | ||
this.parent = parent; | ||
this.buffer = buffer; | ||
this.index = index; | ||
this.start = start; | ||
} | ||
return BufferContext; | ||
}()); | ||
var BufferNode = /** @class */ (function () { | ||
function BufferNode(context, _parent, index) { | ||
this.context = context; | ||
this._parent = _parent; | ||
this.index = index; | ||
this.type = context.buffer.group.types[context.buffer.buffer[index]]; | ||
} | ||
Object.defineProperty(BufferNode.prototype, "name", { | ||
get: function () { return this.type.name; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(BufferNode.prototype, "from", { | ||
get: function () { return this.context.start + this.context.buffer.buffer[this.index + 1]; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(BufferNode.prototype, "to", { | ||
get: function () { return this.context.start + this.context.buffer.buffer[this.index + 2]; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
BufferNode.prototype.child = function (dir, after) { | ||
var buffer = this.context.buffer; | ||
var index = buffer.findChild(this.index + 4, buffer.buffer[this.index + 3], dir, after == -100000000 /* None */ ? -100000000 /* None */ : after - this.context.start); | ||
return index < 0 ? null : new BufferNode(this.context, this, index); | ||
}; | ||
Object.defineProperty(BufferNode.prototype, "firstChild", { | ||
get: function () { return this.child(1, -100000000 /* None */); }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(BufferNode.prototype, "lastChild", { | ||
get: function () { return this.child(-1, -100000000 /* None */); }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
BufferNode.prototype.childAfter = function (pos) { return this.child(1, pos); }; | ||
BufferNode.prototype.childBefore = function (pos) { return this.child(-1, pos); }; | ||
Object.defineProperty(BufferNode.prototype, "parent", { | ||
get: function () { | ||
return this._parent || this.context.parent.nextSignificant(); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
BufferNode.prototype.externalSibling = function (dir) { | ||
return this._parent ? null : this.context.parent.nextChild(this.context.index + dir, dir, -1); | ||
}; | ||
Object.defineProperty(BufferNode.prototype, "nextSibling", { | ||
get: function () { | ||
var buffer = this.context.buffer; | ||
var 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); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(BufferNode.prototype, "prevSibling", { | ||
get: function () { | ||
var buffer = this.context.buffer; | ||
var 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, -1)); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(BufferNode.prototype, "cursor", { | ||
get: function () { return new TreeCursor(this); }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
BufferNode.prototype.resolve = function (pos, side) { | ||
if (side === void 0) { side = 0; } | ||
return this.cursor.moveTo(pos, side).node; | ||
}; | ||
/// @internal | ||
iterInner(from, to, offset, iter) { | ||
if (from <= to) { | ||
for (let index = 0; index < this.buffer.length;) | ||
index = this.iterChild(from, to, offset, index, iter); | ||
BufferNode.prototype.toString = function () { return this.context.buffer.childString(this.index); }; | ||
BufferNode.prototype.getChild = function (type, before, after) { | ||
if (before === void 0) { before = null; } | ||
if (after === void 0) { after = null; } | ||
var r = getChildren(this, type, before, after); | ||
return r.length ? r[0] : null; | ||
}; | ||
BufferNode.prototype.getChildren = function (type, before, after) { | ||
if (before === void 0) { before = null; } | ||
if (after === void 0) { after = null; } | ||
return getChildren(this, type, before, after); | ||
}; | ||
return BufferNode; | ||
}()); | ||
/// A tree cursor object focuses on a given node in a syntax tree, and | ||
/// allows you to move to adjacent nodes. | ||
var TreeCursor = /** @class */ (function () { | ||
/// @internal | ||
function TreeCursor(node) { | ||
this.buffer = null; | ||
this.stack = []; | ||
this.index = 0; | ||
this.bufferNode = null; | ||
if (node instanceof TreeNode) { | ||
this.yieldNode(node); | ||
} | ||
else { | ||
this.iterRev(from, to, offset, 0, this.buffer.length, iter); | ||
this.tree = node.context.parent; | ||
this.buffer = node.context; | ||
for (var n = node._parent; n; n = n._parent) | ||
this.stack.unshift(n.index); | ||
this.bufferNode = node; | ||
this.yieldBuf(node.index); | ||
} | ||
} | ||
Object.defineProperty(TreeCursor.prototype, "name", { | ||
/// Shorthand for `.type.name`. | ||
get: function () { return this.type.name; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
TreeCursor.prototype.yieldNode = function (node) { | ||
if (!node) | ||
return false; | ||
this.tree = node; | ||
this.type = node.type; | ||
this.from = node.from; | ||
this.to = node.to; | ||
return true; | ||
}; | ||
TreeCursor.prototype.yieldBuf = function (index, type) { | ||
this.index = index; | ||
var _a = this.buffer, start = _a.start, buffer = _a.buffer; | ||
this.type = type || buffer.group.types[buffer.buffer[index]]; | ||
this.from = start + buffer.buffer[index + 1]; | ||
this.to = start + buffer.buffer[index + 2]; | ||
return true; | ||
}; | ||
TreeCursor.prototype.yield = function (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 | ||
iterChild(from, to, offset, index, iter) { | ||
let type = this.group.types[this.buffer[index++]], start = this.buffer[index++] + offset, end = this.buffer[index++] + offset, endIndex = this.buffer[index++]; | ||
if (start > to) | ||
return this.buffer.length; | ||
if (end >= from && iter.doEnter(type, start, end)) { | ||
while (index < endIndex && !iter.done) | ||
index = this.iterChild(from, to, offset, index, iter); | ||
if (iter.leave) | ||
iter.leave(type, start, end); | ||
TreeCursor.prototype.toString = function () { | ||
return this.buffer ? this.buffer.buffer.childString(this.index) : this.tree.toString(); | ||
}; | ||
/// @internal | ||
TreeCursor.prototype.enter = function (dir, after) { | ||
if (!this.buffer) | ||
return this.yield(this.tree.nextChild(dir < 0 ? this.tree.node.children.length - 1 : 0, dir, after)); | ||
var buffer = this.buffer.buffer; | ||
var index = buffer.findChild(this.index + 4, buffer.buffer[this.index + 3], dir, after == -100000000 /* None */ ? -100000000 /* None */ : after - this.buffer.start); | ||
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. | ||
TreeCursor.prototype.firstChild = function () { return this.enter(1, -100000000 /* None */); }; | ||
/// Move the cursor to this node's last child. | ||
TreeCursor.prototype.lastChild = function () { return this.enter(-1, -100000000 /* None */); }; | ||
/// Move the cursor to the first child that starts at or after `pos`. | ||
TreeCursor.prototype.childAfter = function (pos) { return this.enter(1, pos); }; | ||
/// Move to the last child that ends at or before `pos`. | ||
TreeCursor.prototype.childBefore = function (pos) { return this.enter(-1, pos); }; | ||
/// Move the node's parent node, if this isn't the top node. | ||
TreeCursor.prototype.parent = function () { | ||
if (!this.buffer) | ||
return this.yieldNode(this.tree.parent); | ||
if (this.stack.length) | ||
return this.yieldBuf(this.stack.pop()); | ||
var parent = this.buffer.parent.nextSignificant(); | ||
this.buffer = null; | ||
return this.yieldNode(parent); | ||
}; | ||
/// @internal | ||
TreeCursor.prototype.sibling = function (dir) { | ||
if (!this.buffer) | ||
return this.tree._parent ? this.yield(this.tree._parent.nextChild(this.tree.index + dir, dir, -100000000 /* None */)) : false; | ||
var buffer = this.buffer.buffer, d = this.stack.length - 1; | ||
if (dir < 0) { | ||
var parentStart = d < 0 ? 0 : this.stack[d] + 4; | ||
if (this.index != parentStart) | ||
return this.yieldBuf(buffer.findChild(parentStart, this.index, -1, -100000000 /* None */)); | ||
} | ||
return endIndex; | ||
} | ||
parentNodesByEnd(startIndex, endIndex) { | ||
// Build up an array of node indices reflecting the order in which | ||
// non-empty nodes end, to avoid having to scan for parent nodes | ||
// at every position during reverse iteration. | ||
let order = []; | ||
let scan = (index) => { | ||
let end = this.buffer[index + 3]; | ||
if (end == index + 4) | ||
return end; | ||
for (let i = index + 4; i < end;) | ||
i = scan(i); | ||
order.push(index); | ||
return end; | ||
}; | ||
for (let index = startIndex; index < endIndex;) | ||
index = scan(index); | ||
return order; | ||
} | ||
/// @internal | ||
iterRev(from, to, offset, startIndex, endIndex, iter) { | ||
let endOrder = this.parentNodesByEnd(startIndex, endIndex); | ||
// Index range for the next non-empty node | ||
let nextStart = -1, nextEnd = -1; | ||
let takeNext = () => { | ||
if (endOrder.length > 0) { | ||
nextStart = endOrder.pop(); | ||
nextEnd = this.buffer[nextStart + 3]; | ||
else { | ||
var after_1 = buffer.buffer[this.index + 3]; | ||
if (after_1 < (d < 0 ? buffer.buffer.length : buffer.buffer[this.stack[d] + 3])) | ||
return this.yieldBuf(after_1); | ||
} | ||
return d < 0 ? this.yield(this.buffer.parent.nextChild(this.buffer.index + dir, dir, -100000000 /* None */)) : false; | ||
}; | ||
/// Move to this node's next sibling, if any. | ||
TreeCursor.prototype.nextSibling = function () { return this.sibling(1); }; | ||
/// Move to this node's previous sibling, if any. | ||
TreeCursor.prototype.prevSibling = function () { return this.sibling(-1); }; | ||
TreeCursor.prototype.atLastNode = function (dir) { | ||
var _a, _b; | ||
var index, parent, buffer = this.buffer; | ||
if (buffer) { | ||
if (dir > 0) { | ||
if (this.index < buffer.buffer.buffer.length) | ||
return false; | ||
} | ||
else { | ||
nextEnd = -1; | ||
for (var i = 0; i < this.index; i++) | ||
if (buffer.buffer.buffer[i + 3] < this.index) | ||
return false; | ||
} | ||
}; | ||
takeNext(); | ||
run: for (let index = endIndex; index > startIndex && !iter.done;) { | ||
while (nextEnd == index) { | ||
let base = nextStart; | ||
let id = this.buffer[base], start = this.buffer[base + 1] + offset, end = this.buffer[base + 2] + offset; | ||
takeNext(); | ||
if (start <= from && end >= to) { | ||
if (!iter.doEnter(this.group.types[id], start, end)) { | ||
// Skip the entire node | ||
index = base; | ||
while (nextEnd > base) | ||
takeNext(); | ||
continue run; | ||
} | ||
} | ||
(index = buffer.index, parent = buffer.parent); | ||
} | ||
else { | ||
(_a = this.tree, index = _a.index, parent = _a._parent); | ||
} | ||
for (; parent; _b = parent, index = _b.index, parent = _b._parent, _b) { | ||
for (var i = index + dir, e = dir < 0 ? -1 : parent.node.children.length; i != e; i += dir) { | ||
var child = parent.node.children[i]; | ||
if (!child.type.isRepeated || child instanceof TreeBuffer || hasChild(child)) | ||
return false; | ||
} | ||
let endIndex = this.buffer[--index], end = this.buffer[--index] + offset, start = this.buffer[--index] + offset, id = this.buffer[--index]; | ||
if (start > from || end < to) | ||
continue; | ||
if ((endIndex != index + 4 || iter.doEnter(this.group.types[id], start, end)) && iter.leave) | ||
iter.leave(this.group.types[id], start, end); | ||
} | ||
} | ||
/// @internal | ||
findIndex(pos, side, start, from, to) { | ||
let lastI = -1; | ||
for (let i = from, buf = this.buffer; i < to;) { | ||
let start1 = buf[i + 1] + start, end1 = buf[i + 2] + start; | ||
let ignore = start1 == end1 && start1 == pos; | ||
if (start1 >= pos) { | ||
if (side > 0 && !ignore) | ||
return i; | ||
return true; | ||
}; | ||
TreeCursor.prototype.move = function (dir) { | ||
if (this.enter(dir, -100000000 /* None */)) | ||
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, its next sibling or the next sibling of | ||
/// the first parent node that has one. | ||
TreeCursor.prototype.next = function () { return this.move(1); }; | ||
/// Move to the next node in a last-to-first pre-order traveral. A | ||
/// node is followed by ist last child or, if it has none, its | ||
/// previous sibling or the previous sibling of the first parent | ||
/// node that has one. | ||
TreeCursor.prototype.prev = function () { return this.move(-1); }; | ||
/// 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`. | ||
TreeCursor.prototype.moveTo = function (pos, side) { | ||
if (side === void 0) { 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 | ||
for (;;) { | ||
if (side < 0 ? !this.childBefore(pos) : !this.childAfter(pos)) | ||
break; | ||
if (this.from == this.to || | ||
(side < 1 ? this.from >= pos : this.from > pos) || | ||
(side > -1 ? this.to <= pos : this.to < pos)) { | ||
this.parent(); | ||
break; | ||
} | ||
if (end1 > pos) | ||
return i; | ||
if (!ignore) | ||
lastI = i; | ||
i = buf[i + 3]; | ||
} | ||
return side < 0 ? lastI : -1; | ||
} | ||
return this; | ||
}; | ||
Object.defineProperty(TreeCursor.prototype, "node", { | ||
/// Get a [syntax node](#tree.SyntaxNode) at the cursor's current | ||
/// position. | ||
get: function () { | ||
if (!this.buffer) | ||
return this.tree; | ||
var cache = this.bufferNode, result = null, depth = 0; | ||
if (cache && cache.context == this.buffer) { | ||
scan: for (var index = this.index, d = this.stack.length; d >= 0;) { | ||
for (var 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 (var 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); | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
return TreeCursor; | ||
}()); | ||
function hasChild(tree) { | ||
return tree.children.some(function (ch) { return !ch.type.isRepeated || ch instanceof TreeBuffer || hasChild(ch); }); | ||
} | ||
class NodeSubtree extends Subtree { | ||
constructor(node, start, parent) { | ||
super(); | ||
this.node = node; | ||
this.start = start; | ||
this.parent = parent; | ||
} | ||
get type() { return this.node.type; } | ||
get end() { return this.start + this.node.length; } | ||
resolveAt(pos) { | ||
if (pos <= this.start || pos >= this.end) | ||
return this.parent.resolveAt(pos); | ||
return this.node.resolveInner(pos, this.start, this); | ||
} | ||
childBefore(pos) { | ||
return this.node.findChild(pos, -1, this.start, this); | ||
} | ||
childAfter(pos) { | ||
return this.node.findChild(pos, 1, this.start, this); | ||
} | ||
toString() { return this.node.toString(); } | ||
iterate({ from = this.start, to = this.end, enter, leave }) { | ||
let iter = new Iteration(enter, leave); | ||
this.node.iterInner(from, to, this.start, iter); | ||
return iter.result; | ||
} | ||
} | ||
class BufferSubtree extends Subtree { | ||
constructor(buffer, bufferStart, index, parent) { | ||
super(); | ||
var FlatBufferCursor = /** @class */ (function () { | ||
function FlatBufferCursor(buffer, index) { | ||
this.buffer = buffer; | ||
this.bufferStart = bufferStart; | ||
this.index = index; | ||
this.parent = parent; | ||
} | ||
get type() { return this.buffer.group.types[this.buffer.buffer[this.index]]; } | ||
get start() { return this.buffer.buffer[this.index + 1] + this.bufferStart; } | ||
get end() { return this.buffer.buffer[this.index + 2] + this.bufferStart; } | ||
get endIndex() { return this.buffer.buffer[this.index + 3]; } | ||
childBefore(pos) { | ||
let index = this.buffer.findIndex(pos, -1, this.bufferStart, this.index + 4, this.endIndex); | ||
return index < 0 ? null : new BufferSubtree(this.buffer, this.bufferStart, index, this); | ||
} | ||
childAfter(pos) { | ||
let index = this.buffer.findIndex(pos, 1, this.bufferStart, this.index + 4, this.endIndex); | ||
return index < 0 ? null : new BufferSubtree(this.buffer, this.bufferStart, index, this); | ||
} | ||
iterate({ from = this.start, to = this.end, enter, leave }) { | ||
let iter = new Iteration(enter, leave); | ||
if (from <= to) | ||
this.buffer.iterChild(from, to, this.bufferStart, this.index, iter); | ||
else | ||
this.buffer.iterRev(from, to, this.bufferStart, this.index, this.endIndex, iter); | ||
return iter.result; | ||
} | ||
resolveAt(pos) { | ||
if (pos <= this.start || pos >= this.end) | ||
return this.parent.resolveAt(pos); | ||
let found = this.buffer.findIndex(pos, 0, this.bufferStart, this.index + 4, this.endIndex); | ||
return found < 0 ? this : new BufferSubtree(this.buffer, this.bufferStart, found, this).resolveAt(pos); | ||
} | ||
toString() { | ||
let result = []; | ||
this.buffer.childToString(this.index, result); | ||
return result.join(""); | ||
} | ||
} | ||
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); } | ||
} | ||
const BalanceBranchFactor = 8; | ||
Object.defineProperty(FlatBufferCursor.prototype, "id", { | ||
get: function () { return this.buffer[this.index - 4]; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(FlatBufferCursor.prototype, "start", { | ||
get: function () { return this.buffer[this.index - 3]; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(FlatBufferCursor.prototype, "end", { | ||
get: function () { return this.buffer[this.index - 2]; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(FlatBufferCursor.prototype, "size", { | ||
get: function () { return this.buffer[this.index - 1]; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(FlatBufferCursor.prototype, "pos", { | ||
get: function () { return this.index; }, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
FlatBufferCursor.prototype.next = function () { this.index -= 4; }; | ||
FlatBufferCursor.prototype.fork = function () { return new FlatBufferCursor(this.buffer, this.index); }; | ||
return FlatBufferCursor; | ||
}()); | ||
var BalanceBranchFactor = 8; | ||
function buildTree(data) { | ||
let { buffer, group, topID = 0, maxBufferLength = DefaultBufferLength, reused = [], minRepeatType = group.types.length } = data; | ||
let cursor = Array.isArray(buffer) ? new FlatBufferCursor(buffer, buffer.length) : buffer; | ||
let types = group.types; | ||
var _a = data, buffer = _a.buffer, group = _a.group, _b = _a.topID, topID = _b === void 0 ? 0 : _b, _c = _a.maxBufferLength, maxBufferLength = _c === void 0 ? DefaultBufferLength : _c, _d = _a.reused, reused = _d === void 0 ? [] : _d, _e = _a.minRepeatType, minRepeatType = _e === void 0 ? group.types.length : _e; | ||
var cursor = Array.isArray(buffer) ? new FlatBufferCursor(buffer, buffer.length) : buffer; | ||
var types = group.types; | ||
function takeNode(parentStart, minPos, children, positions, inRepeat) { | ||
let { id, start, end, size } = cursor; | ||
var id = cursor.id, start = cursor.start, end = cursor.end, size = cursor.size; | ||
while (id == inRepeat) { | ||
cursor.next(); | ||
({ id, start, end, size } = cursor); | ||
(id = cursor.id, start = cursor.start, end = cursor.end, size = cursor.size); | ||
} | ||
let startPos = start - parentStart; | ||
var startPos = start - parentStart; | ||
if (size < 0) { // Reused node | ||
@@ -711,17 +987,17 @@ children.push(reused[id]); | ||
} | ||
let type = types[id], node, buffer; | ||
var type = types[id], node, buffer; | ||
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; | ||
var data_1 = new Uint16Array(buffer.size - buffer.skip); | ||
var endPos = cursor.pos - buffer.size, index = data_1.length; | ||
while (cursor.pos > endPos) | ||
index = copyToBuffer(buffer.start, data, index, inRepeat); | ||
node = new TreeBuffer(data, end - buffer.start, group, inRepeat < 0 ? NodeType.none : types[inRepeat]); | ||
index = copyToBuffer(buffer.start, data_1, index, inRepeat); | ||
node = new TreeBuffer(data_1, end - buffer.start, group, inRepeat < 0 ? NodeType.none : types[inRepeat]); | ||
startPos = buffer.start - parentStart; | ||
} | ||
else { // Make it a node | ||
let endPos = cursor.pos - size; | ||
var endPos = cursor.pos - size; | ||
cursor.next(); | ||
let localChildren = [], localPositions = []; | ||
let localInRepeat = id >= minRepeatType ? id : -1; | ||
var localChildren = [], localPositions = []; | ||
var localInRepeat = id >= minRepeatType ? id : -1; | ||
while (cursor.pos > endPos) | ||
@@ -746,6 +1022,6 @@ takeNode(start, endPos, localChildren, localPositions, localInRepeat); | ||
// (`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;) { | ||
var fork = cursor.fork(); | ||
var size = 0, start = 0, skip = 0, minStart = fork.end - maxBufferLength; | ||
var result = { size: 0, start: 0, skip: 0 }; | ||
scan: for (var minPos = fork.pos - maxSize; fork.pos > minPos;) { | ||
// Pretend nested repeat nodes of the same type don't exist | ||
@@ -763,7 +1039,7 @@ if (fork.id == inRepeat) { | ||
} | ||
let nodeSize = fork.size, startPos = fork.pos - nodeSize; | ||
var nodeSize = fork.size, startPos = fork.pos - nodeSize; | ||
if (nodeSize < 0 || startPos < minPos || fork.start < minStart) | ||
break; | ||
let localSkipped = fork.id >= minRepeatType ? 4 : 0; | ||
let nodeStart = fork.start; | ||
var localSkipped = fork.id >= minRepeatType ? 4 : 0; | ||
var nodeStart = fork.start; | ||
fork.next(); | ||
@@ -789,9 +1065,9 @@ while (fork.pos > startPos) { | ||
function copyToBuffer(bufferStart, buffer, index, inRepeat) { | ||
let { id, start, end, size } = cursor; | ||
var id = cursor.id, start = cursor.start, end = cursor.end, size = cursor.size; | ||
cursor.next(); | ||
if (id == inRepeat) | ||
return index; | ||
let startIndex = index; | ||
var startIndex = index; | ||
if (size > 4) { | ||
let endPos = cursor.pos - (size - 4); | ||
var endPos = cursor.pos - (size - 4); | ||
while (cursor.pos > endPos) | ||
@@ -808,12 +1084,12 @@ index = copyToBuffer(bufferStart, buffer, index, inRepeat); | ||
} | ||
let children = [], positions = []; | ||
var children = [], positions = []; | ||
while (cursor.pos > 0) | ||
takeNode(0, 0, children, positions, -1); | ||
let length = children.length ? positions[0] + children[0].length : 0; | ||
var length = children.length ? positions[0] + children[0].length : 0; | ||
return new Tree(group.types[topID], children.reverse(), positions.reverse(), length); | ||
} | ||
function balanceRange(outerType, innerType, children, positions, from, to, start, maxBufferLength, length) { | ||
let localChildren = [], localPositions = []; | ||
var localChildren = [], localPositions = []; | ||
if (length <= maxBufferLength) { | ||
for (let i = from; i < to; i++) { | ||
for (var i = from; i < to; i++) { | ||
localChildren.push(children[i]); | ||
@@ -824,8 +1100,8 @@ localPositions.push(positions[i] - start); | ||
else { | ||
let maxChild = Math.max(maxBufferLength, Math.ceil(length * 1.5 / BalanceBranchFactor)); | ||
for (let i = from; i < to;) { | ||
let groupFrom = i, groupStart = positions[i]; | ||
var maxChild = Math.max(maxBufferLength, Math.ceil(length * 1.5 / BalanceBranchFactor)); | ||
for (var i = from; i < to;) { | ||
var groupFrom = i, groupStart = positions[i]; | ||
i++; | ||
for (; i < to; i++) { | ||
let nextEnd = positions[i] + children[i].length; | ||
var nextEnd = positions[i] + children[i].length; | ||
if (nextEnd - groupStart > maxChild) | ||
@@ -835,5 +1111,5 @@ break; | ||
if (i == groupFrom + 1) { | ||
let only = children[groupFrom]; | ||
var only = children[groupFrom]; | ||
if (only instanceof Tree && only.type == innerType && only.length > maxChild << 1) { // Too big, collapse | ||
for (let j = 0; j < only.children.length; j++) { | ||
for (var j = 0; j < only.children.length; j++) { | ||
localChildren.push(only.children[j]); | ||
@@ -850,3 +1126,3 @@ localPositions.push(only.positions[j] + groupStart - start); | ||
else { | ||
let inner = balanceRange(innerType, innerType, children, positions, groupFrom, i, groupStart, maxBufferLength, positions[i - 1] + children[i - 1].length - groupStart); | ||
var inner = balanceRange(innerType, innerType, children, positions, groupFrom, i, groupStart, maxBufferLength, positions[i - 1] + children[i - 1].length - groupStart); | ||
if (innerType != NodeType.none && !containsType(inner.children, innerType)) | ||
@@ -862,6 +1138,11 @@ inner = new Tree(NodeType.none, inner.children, inner.positions, inner.length); | ||
function containsType(nodes, type) { | ||
for (let elt of nodes) | ||
for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) { | ||
var elt = nodes_1[_i]; | ||
if (elt.type == type) | ||
return true; | ||
} | ||
return false; | ||
} | ||
export { DefaultBufferLength, NodeGroup, NodeProp, NodePropSource, NodeType, Tree, TreeBuffer, TreeCursor }; | ||
//# sourceMappingURL=tree.es.js.map |
{ | ||
"name": "lezer-tree", | ||
"version": "0.11.1", | ||
"version": "0.12.0", | ||
"description": "Syntax tree data structure for the lezer parser", | ||
@@ -17,3 +17,10 @@ "main": "dist/tree.cjs", | ||
"ist": "^1.1.1", | ||
"typescript": "^3.7.2" | ||
"rollup": "^2.27.1", | ||
"@rollup/plugin-commonjs": "^15.1.0", | ||
"@rollup/plugin-node-resolve": "^9.0.0", | ||
"rollup-plugin-typescript2": "^0.27.2", | ||
"typescript": "^3.7.2", | ||
"@types/mocha": "^5.2.6", | ||
"ts-node": "^8.0.3", | ||
"mocha": "^8.1.3" | ||
}, | ||
@@ -26,7 +33,6 @@ "files": ["dist"], | ||
"scripts": { | ||
"watch": "tsc --outDir dist -w -d", | ||
"build:cjs": "tsc --outDir dist -d && mv dist/tree.js dist/tree.cjs", | ||
"build:es": "tsc src/tree.ts --outDir . -m es2015 -t es2015 && mv tree.js dist/tree.es.js", | ||
"prepare": "npm run build:cjs && npm run build:es" | ||
"watch": "rollup -w -c rollup.config.js", | ||
"prepare": "rollup -c rollup.config.js", | ||
"test": "mocha -r ts-node/register/transpile-only test/test-*.ts" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
266233
9
2383
9