Provide an iterator for a node's children and descendants?

To optimise methods that search a node’s children or descendants for a matching node, it would be useful to have an iterator that can break when the matching node is found.

For instance, instead of doing this:

const findDescendantOfType = (node: ProsemirrorNode, type: string) => {
  let output = null

  node.descendants(child => {
    if (child.type.name === type) {
      output = child
    }
  })

  return output
}

I’d like to do this:

const findDescendantOfType = (node: ProsemirrorNode, type: string) => {
  for (const child of node.descendants) {
      if (child.type.name === type) {
         return child
      }
  }

  return null
}

It looks like there used to be an iter method, which was then replaced with methods to iterate through children by index:

const findChildOfType = (node: ProsemirrorNode, type: string) => {
    for (let i = 0; i < node.childCount; i++) {
       const child = node.child(i)

       if (child.type.name === type) {
         return child
       }
   }

  return null
}

This is reasonable - if a bit verbose - for the above example, but gets more complicated when iterating through all a node’s descendants.

Would it make sense to restore a way of getting iterators for a node’s children and descendants?

It would also be nice if the iterator could be used with Array.from(), to allow methods like filter and map to be used on the result.

1 Like

I implemented an iterator using a simple generator function (below), and it turned out that more often than not I needed the offset or position that are passed as parameters to the forEach or descendants callback, so feel free to disregard the request above :slightly_smiling_face:

function* iterateChildren(node) {
  for (let i = 0; i < node.childCount; i++) {
   yield node.child(i)
  }
}

Does it help to know that the callback passed to descendants can just continue returning false to abort the iteration early? (So you’d start it with a check like if (output != null) return false)).

Does it help to know that the callback passed to descendants can just continue returning false to abort the iteration early?

I’ve used return false to stop descending into a node but hadn’t thought of continuing to return false - that could be useful.