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


#1

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.


#2

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)
  }
}

#3

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)).


#4

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.