How to get changed range on transaction.

In ProseMirror many times plugins may need to know the changed regions to update it’s internal state. For example tiptap-unique-id extension uses findChildrenInRange to get a list of ranges to get the changed nodes. In other scenarios plugins can have state dependencies and may keep computed properties based on nodes. When a transaction happens the plugin has two option

  1. Create the internal state from scratch
  2. Reuse some of the old state and incrementally update

Currently I am using the following code to get the old and new range.

function getReplacedRange(tr: Transaction) {
  if (!tr.docChanged) {
    return null
  }

  let fromA = Infinity
  let toA = -Infinity
  let fromB = Infinity
  let toB = -Infinity

  // Get bounds in the original document by mapping each step's range backward
  tr.mapping.maps.forEach((map, i) => {
    map.forEach((from, to) => {
      const newStart = tr.mapping.slice(i).map(from, -1)
      const newEnd = tr.mapping.slice(i).map(to)
      const oldStart = tr.mapping.invert().map(newStart, -1)
      const oldEnd = tr.mapping.invert().map(newEnd)

      fromA = min(fromA, oldStart, oldEnd)
      toA = max(toA, oldStart, oldEnd)
      fromB = min(fromB, newStart, newEnd)
      toB = max(toB, newEnd, newStart)
    })
  })

  const delta = toB - fromB - (toA - fromA)

  // range (fromA, toA) is replaced by (fromB, toB) in new document
  return { fromA, toA, fromB, toB, delta }
}

Are there any better approaches? Having the plugins depend on each others state is a good design?

Are there any better approaches?

I went ahead and added this as a utility method (changedRanges()) in prosemirror-transform 1.11.0. I’m not sure your version is correct—it looks like it is mapping the old positions through too many steps.

I’m not sure what you mean here. What other plugin’s state is involved?

Thanks for the update.

I also wanted to have more information from a transaction. If the touchedRange from prosemirror-changeset package was public it would have been enough.