Skip to content

Commit

Permalink
Add support for returning a new index from visitor
Browse files Browse the repository at this point in the history
Closes GH-9.
  • Loading branch information
wooorm committed Dec 10, 2017
1 parent ced8a78 commit 5eeb002
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 8 deletions.
12 changes: 5 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,30 +36,28 @@ function visit(tree, test, visitor, reverse) {
}

if (node.children && result !== SKIP) {
return all(node.children, node);
return all(node.children, node) === EXIT ? EXIT : result;
}

return CONTINUE;
return result;
}

/* Visit children in `parent`. */
function all(children, parent) {
var step = reverse ? -1 : 1;
var max = children.length;
var min = -1;
var index = (reverse ? max : min) + step;
var index = (reverse ? children.length : -1) + step;
var child;
var result;

while (index > min && index < max) {
while (index > -1 && index < children.length) {
child = children[index];
result = child && one(child, index, parent);

if (result === EXIT) {
return result;
}

index += step;
index = typeof result === 'number' ? result : index + step;
}

return CONTINUE;
Expand Down
16 changes: 15 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,13 @@ filtering nodes. Optionally in reverse.

Invoked when a node (matching `test` if given) is found.

You can transform visited nodes.
You can transform visited nodes. You can transform `parent`, but if adding or
removing [**children**][child] before `index`, you should return a new `index`
(`number`) from `visitor` to specify the next sibling to visit. Replacing
`node` itself still causes its descendants to be visited. Adding or removing
nodes after `index` is handled as expected without needing to return a new
`index`. Remove the `children` property on `parent` still results in them
being traversed.

###### Parameters

Expand All @@ -79,6 +85,12 @@ You can transform visited nodes.
* `visit.SKIP` (`'skip'`)
— Do not enter this node (traversing into its children), but do continue
with the next sibling
* `index` (`number`)
— Move to the sibling at position `index` next (after `node` itself is
traversed). Useful if you’re mutating the tree (such as removing the node
you’re currently on, or any of its preceding siblings). Results less than
`0` or greater than or equal to `children.length` stop iteration of the
parent

## Related

Expand Down Expand Up @@ -133,6 +145,8 @@ repository, organisation, or community you agree to abide by its terms.

[descendant]: https://github.com/syntax-tree/unist#descendant

[child]: https://github.com/syntax-tree/unist#child

[is]: https://github.com/syntax-tree/unist-util-is#istest-node-index-parent-context

[visitor]: #next--visitornode-index-parent
Expand Down
116 changes: 116 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,5 +291,121 @@ test('unist-util-visit', function (t) {
}
});

t.test('should support a given `index` to iterate over next (`0` to reiterate)', function (st) {
var n = 0;
var again = false;
var expected = [
'root',
'paragraph',
'text',
'emphasis',
'text',
'text',
'strong',
'text',
'text', /* Again. */
'emphasis',
'text',
'text',
'strong',
'text',
'text',
'inlineCode',
'text'
];

st.doesNotThrow(
function () {
visit(tree, visitor);
},
'should visit nodes again (#1)'
);

st.equal(n, expected.length, 'should visit nodes again (#2)');

st.end();

function visitor(node) {
assert.equal(node.type, expected[n++], 'should be the expected type');

if (again === false && node.type === 'strong') {
again = true;
return 0; /* Start over. */
}
}
});

t.test('should support a given `index` to iterate over next (`children.length` to skip further children)', function (st) {
var n = 0;
var again = false;
var expected = [
'root',
'paragraph',
'text',
'emphasis',
'text',
'text',
'strong', /* Skip here. */
'text'
];

st.doesNotThrow(
function () {
visit(tree, visitor);
},
'should skip nodes (#1)'
);

st.equal(n, expected.length, 'should skip nodes (#2)');

st.end();

function visitor(node, index, parent) {
assert.equal(node.type, expected[n++], 'should be the expected type');

if (again === false && node.type === 'strong') {
again = true;
return parent.children.length; /* Skip siblings. */
}
}
});

t.test('should support any other given `index` to iterate over next', function (st) {
var n = 0;
var again = false;
var expected = [
'root',
'paragraph',
'text',
'emphasis',
'text',
'text',
'strong',
'text',
'inlineCode', /* Skip to here. */
'text'
];

st.doesNotThrow(
function () {
visit(tree, visitor);
},
'should skip nodes (#1)'
);

st.equal(n, expected.length, 'should skip nodes (#2)');

st.end();

function visitor(node, index) {
assert.equal(node.type, expected[n++], 'should be the expected type');

if (again === false && node.type === 'strong') {
again = true;
return index + 2; /* Skip to `inlineCode`. */
}
}
});

t.end();
});

0 comments on commit 5eeb002

Please sign in to comment.