Set attribute on all selected nodes?

What is the preferred method for setting an attribute on the nodes in the current selection?

I’m looking at doing something like this, but it feels a bit…unnatural.

startSlice = pm.doc.resolve(pm.selection.from).pos - pm.doc.resolve(pm.selection.from).parentOffset
endSlice =  pm.doc.resolve(pm.selection.to).pos - pm.doc.resolve(pm.selection.to).parentOffset + pm.doc.resolve(pm.selection.to).parent.content.size + 1
pm.doc.forEach(((node, offset, index) =>
  if offset < startSlice || offset > endSlice
    return
  newAttrs = Object.assign(node.attrs)
  newAttrs['some_attribute'] = 'some_value'
  if node.isTextblock
    pm.tr.setBlockType( offset, node.content.size, node.type, newAttrs ).apply()
  else
    from = this.editor.selection.from
    pm.tr.setNodeType( offset, node.type, newAttrs ).apply()
).bind(this))

What is the role of the attribute you’re trying to set?

What is the role of the attribute you’re trying to set?

The attribute is for text alignment

Ah, I see. So you want to make a change to all applicable blocks touched by the selection?

(For one thing, doing doc.resolve(x).pos is just going to give you x, so don’t do that.)

Something like this might work (untested):

function setAlign(pm, align) {
  let tr = pm.tr
  pm.doc.nodesBetween(pm.selection.from, pm.selection.to, (node, pos) => {
    if (typeSupportsAlign(node.type) && node.attrs.align != align)
      tr.setNodeType(pos, node.type, Object.assign({}, node.attrs, {align}))
  })
  tr.apply()
}
1 Like

Thanks, this is much cleaner than what I was thinking.

The document doesn’t re-render itself after applying that transform though…eg. if I open the basic demo, place my caret in the first heading node, and run this in my JS console

var tr = pm.tr
pm.doc.nodesBetween(pm.selection.from, pm.selection.to, (node, pos) => {
  if (node.type.attrs.level) {
    newAttrs = Object.assign(node.attrs)
    newAttrs.level = 3
    tr.setNodeType(pos, node.type, newAttrs)
  }
})
tr.apply()

I can inspect the document (window.pm.doc) and see that the document has changed appropriately, but hasn’t rerendered the node as an h3.

I don’t think Object.assign, when used like that, is going to copy the object. So you end up mutating the original object and the diffing algorithm that does the redraw isn’t going to notice that the document changed.

Ah yes, that seems to be the case. This works, thanks again!

var tr = pm.tr
pm.doc.nodesBetween(pm.selection.from, pm.selection.to, (node, pos) => {
  if (node.type.attrs.level) {
    newAttrs = Object.assign({}, node.attrs)
    newAttrs.level = 3
    tr.setNodeType(pos, node.type, newAttrs)
  }
})
tr.apply()
1 Like