How to `setNodeMarkup` on a `text` Node

Here is a plugin taken from https://discuss.prosemirror.net/t/how-i-can-attach-attribute-with-dynamic-value-when-new-paragraph-is-inserted/751/3

new Plugin({
        key: PLUGIN_KEY,
        appendTransaction: (transactions, oldState, newState) => {
          const tr = newState.tr
          let modified = false
          if (transactions.some(transaction => transaction.docChanged)) {
            newState.doc.descendants((node, pos) => {
              console.log(`${node.type.name}`)
              const { uuid: id, ...rest } = node.attrs
              if (!id) {
                // Id is not set or already taken => generate and set a new id
                const newId = uuid()
                tr.setNodeMarkup(pos, null, { uuid: newId, ...rest })
                modified = true
              }
            })
          }

          if (modified) return tr
        }
      })

But it says

Uncaught Error: NodeType.create can't construct text nodes
    at NodeType.create (index.es.js:1965)
    at Transaction.Transform.setNodeMarkup (index.es.js:765)
    at index.ts:26

It seems that a text type cannot create in setNodeMarkup:

// :: (number, ?NodeType, ?Object, ?[Mark]) → this
// Change the type, attributes, and/or marks of the node at `pos`.
// When `type` isn't given, the existing node type is preserved,
Transform.prototype.setNodeMarkup = function(pos, type, attrs, marks) {
  let node = this.doc.nodeAt(pos)
  if (!node) throw new RangeError("No node at given position")
  if (!type) type = node.type
  let newNode = type.create(attrs, null, marks || node.marks)
  if (node.isLeaf)
    return this.replaceWith(pos, pos + node.nodeSize, newNode)

  if (!type.validContent(node.content))
    throw new RangeError("Invalid content for node type " + type.name)

  return this.step(new ReplaceAroundStep(pos, pos + node.nodeSize, pos + 1, pos + node.nodeSize - 1,
                                         new Slice(Fragment.from(newNode), 0, 0), 1, true))
}
  // :: (?Object, ?union<Fragment, Node, [Node]>, ?[Mark]) → Node
  // Create a `Node` of this type. The given attributes are
  // checked and defaulted (you can pass `null` to use the type's
  // defaults entirely, if no required attributes exist). `content`
  // may be a `Fragment`, a node, an array of nodes, or
  // `null`. Similarly `marks` may be `null` to default to the empty
  // set of marks.
  create(attrs, content, marks) {
    if (this.isText) throw new Error("NodeType.create can't construct text nodes")
    return new Node(this, this.computeAttrs(attrs), Fragment.from(content), Mark.setFrom(marks))
  }

你参考的帖子,好像人家判断节点类型然后再赋值的。

const isTargetNodeOfType = (node, type) => (node.type === type);

人家是判断段落才会执行。你好像什么节点都运行这个,包括文本节点

1 Like

This isn’t possible—text nodes are different from other nodes, and don’t have a type or attributes. You’ll have to wrap them in some node if you want to use setNodeMarkup, or use mark add/remove steps if you want to change the set of marks on the text.

2 Likes