Helpers for transforming nodes?

While the existing transform helpers are generally useful and good for inserting new elements or modifying large ranges of the document, they’re not very nice for changing individual nodes. Usually this involves searching through the document tree for the node you want, recording the index of the node and then using various helpers. The work you have to do with indexes and nodeSizes seems like bookkeeping for what seems like it should be a straightforward and common action.

Some helpers I’ve written/am currently writing are:

  • modifyNode(state, node, attrs) -> transaction : Given the editor state, a node and a set of attributes, return a transaction that modifies the attributes of that node.
  • addChild(state, node, child) -> transaction : Given the editor state, a node and a child node, return a transaction that adds the child to the contents of the node.
  • remove(state, node) -> transaction : Given the editor state and a node, return a transaction that removes that node and all of its children from the document of that state.
  • changeText(state, node, text) -> transaction : Given the editor state, a text node and a text value, return a transaction that modifies the text of that node.
  • findNodes(root, query): -> [{index, node}] Given a root node and a query function (that takes a node and returns true if it is a node you want to find), return an array of the found nodes with their index (relative to the state document) and the actual node value. Usually used to find children before doing modifications.

All of these could optionally take a transaction in as the argument, in order to chain transactions.

Some helpers similar to this would be very useful to include in the Transformation class. I would contribute what I have but the actual lines of code are fairly small and I’m not sure I’m well acquainted enough with the state model to do it in the best way.

You should not be working with nodes ‘by identity’. A single node object may occur in the document multiple times, or be replaced by a new object when its content changes.

When the user tries to do something to a node, you typically have its position – either from the selection, or from the mouse cursor. That’s why the transform methods take positions, not node objects.

Your modifyNode is covered by setNodeType, addChild, remove, and changeText by insert, delete, and replace, and findNodes is pretty-much a three-liner on top of the descendants method.

I didn’t realize a node could be place multiples times in document, how does that happen, with a copy-paste?

I had this thought again within the context of a nodeview managing its own content nodes. With a nodeview we only start with the position from getPos() and need to keep track of indexes to update children components.

I agree that all of these are pretty simple when you understand the mechanisms, but was just saying that the API could maybe be made simpler e.g. changing this from delete(nodeIndex, nodeIndex + node.nodeSize) to delete(node). But the fact that nodes are not unique in a document makes this difficult.

If you pass the same node to, say, insert, multiple times, that works fine, since they are immutable. I don’t think this can currently happen with copy paste, since it goes through the clipboard text, but drag-drop could cause it.