Inner workings of setBlockType

I’m trying to understand the inner workings of prosemirror so i can write my own functions with more confidence.

this.doc.nodesBetween(from, to, (node, pos) => {
if (node.isTextblock && !node.hasMarkup(type, attrs) && canChangeType(this.doc, this.mapping.slice(mapFrom).map(pos), type)) {

why does canChangeType make a detour to get the parent and index of node when nodesBetween provides parent and index?

function canChangeType(doc, pos, type) {
  let $pos = doc.resolve(pos), index = $pos.index()
  return $pos.parent.canReplaceWith(index, index + 1, type)

and why does setNodeMarkup that does pretty much the same thing not check if the change is valid for the parent?

Where’s the detour? Doing this with nodesBetween seems like it’ll be both more code and more error-prone, since that’ll call your callback for all nodes in a range, not just the node you’re interested in.

Might be an oversight.

I mean canChangeType looks up the parent and index of the current node, but nodesBetween already provides the parent and index of the node?

you can write

this.doc.nodesBetween(from, to, (node, pos, parent, index) => { parent.canReplaceWith(index, index + 1, type)

why does canChangeType resolve the position to get parent and index when it’s already provided in nodesBetween?

Ah, you mean the caller would have those values already. I think that’s because passing the index to the callback wasn’t something nodesBetween did, initially, and this code was written against the old interface. Feel free to open a pull request to optimize that.

I was wrong, I understand now why the detour is needed. The parent from nodesBetween is from the original state and the lookup is needed to get the parent from the current state if previous steps changed the content of the parent.

But nice to learn how the code works.