Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

upgrade descendants and nodesBetween handlers to support aborting a graph scan #75

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions src/fragment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,40 @@ export class Fragment {
/// Invoke a callback for all descendant nodes between the given two
/// positions (relative to start of this fragment). Doesn't descend
/// into a node when the callback returns `false`.
nodesBetween(from: number, to: number,
f: (node: Node, start: number, parent: Node | null, index: number) => boolean | void,
nodesBetween<TResult>(from: number, to: number,
f: (node: Node, start: number, parent: Node | null, index: number) => boolean | void | TResult,
nodeStart = 0,
parent?: Node) {
parent?: Node): void | TResult {
for (let i = 0, pos = 0; pos < to; i++) {
let child = this.content[i], end = pos + child.nodeSize
if (end > from && f(child, nodeStart + pos, parent || null, i) !== false && child.content.size) {
let child = this.content[i], end = pos + child.nodeSize;
// res === undefined || res === null || res === true; then; search children
// res === false; then; keep searching siblings, skip children
// res === Anything Else; then; stop searching immediately, and return Anything as TResult
const res = f(child, nodeStart + pos, parent || null, i);

if (end > from && child.content.size && (res === undefined || res === null || res === true)) {
let start = pos + 1
child.nodesBetween(Math.max(0, from - start),
const cr = child.nodesBetween<TResult>(Math.max(0, from - start),
Math.min(child.content.size, to - start),
f, nodeStart + start)
f, nodeStart + start);
if (!!cr) {
return cr as TResult;
}
} else if (res !== false) {
// If false is returned then skip children and keep searching siblings. If anything else is returned,
// then the search will end and the result will be returned.
return res as TResult;
}

pos = end
}
}

/// Call the given callback for every descendant node. `pos` will be
/// relative to the start of the fragment. The callback may return
/// `false` to prevent traversal of a given node's children.
descendants(f: (node: Node, pos: number, parent: Node | null) => boolean | void) {
this.nodesBetween(0, this.size, f)
descendants<TResult>(f: (node: Node, pos: number, parent: Node | null) => boolean | void | TResult): void | TResult {
return this.nodesBetween(0, this.size, f);
}

/// Extract the text between `from` and `to`. See the same method on
Expand Down
12 changes: 6 additions & 6 deletions src/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,16 @@ export class Node {
/// When the callback returns false for a given node, that node's
/// children will not be recursed over. The last parameter can be
/// used to specify a starting position to count from.
nodesBetween(from: number, to: number,
f: (node: Node, pos: number, parent: Node | null, index: number) => void | boolean,
startPos = 0) {
this.content.nodesBetween(from, to, f, startPos, this)
nodesBetween<TResult>(from: number, to: number,
f: (node: Node, pos: number, parent: Node | null, index: number) => void | boolean | TResult,
startPos = 0): void | TResult {
return this.content.nodesBetween<TResult>(from, to, f, startPos, this);
}

/// Call the given callback for every descendant node. Doesn't
/// descend into a node when the callback returns `false`.
descendants(f: (node: Node, pos: number, parent: Node | null, index: number) => void | boolean) {
this.nodesBetween(0, this.content.size, f)
descendants<TResult>(f: (node: Node, pos: number, parent: Node | null, index: number) => void | boolean | TResult): void | TResult {
return this.nodesBetween<TResult>(0, this.content.size, f);
}

/// Concatenates all the text nodes found in this fragment and its
Expand Down