# How to calculate the changed ranges for transactions

**URL:** <https://discuss.prosemirror.net/t/how-to-calculate-the-changed-ranges-for-transactions/3771>\
**Category:** Uncategorized\
**Created:** [May 15, 2021, 9:33am UTC](https://discuss.prosemirror.net/t/how-to-calculate-the-changed-ranges-for-transactions/3771 "2021-05-15T09:33:14Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![philippkuehn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/philippkuehn/32/1820_2.png) [@philippkuehn](https://discuss.prosemirror.net/u/philippkuehn)\
**Post date:** [May 15, 2021, 9:33am UTC](https://discuss.prosemirror.net/t/how-to-calculate-the-changed-ranges-for-transactions/3771/1 "2021-05-15T09:33:15Z")

</div>

I want to calculate the changed ranges for transactions (even with multiple steps).

I’m aware of `findDiffStart` and `findDiffEnd` but that’s not enough if a change something at the start and end of the document within one transaction.

```auto
let diffStart = oldState.doc.content.findDiffStart(newState.doc.content)
let diffEnd = oldState.doc.content.findDiffEnd(newState.doc.content)

```

With `step.getMap()` I can get the changed ranges of each step but these positions depend on the current doc of the step (`transaction.docs[stepIndex]`).

That’s why I thought I could map these positions forwards and backwards based on its index so that I get the correct positions for the `oldState` and `newState` of the whole transaction.

```auto
transaction.steps.forEach((step, index) => {

  const upcomingMapping = transaction.mapping
    .slice(index, transaction.mapping.maps.length)

  const pastMapping = transaction.mapping
    .slice(0, index)
    .invert()

  step.getMap().forEach((oldStart, oldEnd, newStart, newEnd) => {
    const oldStart = pastMapping.map(oldStart, -1)
    const oldEnd = pastMapping.map(oldEnd)
    const newStart = upcomingMapping.map(oldStart, -1)
    const newEnd = upcomingMapping.map(oldEnd)
  })

})

```

That’s working fine for “normal” changes but breaks (out of range) if I undo changes with the history plugin. So I guess it’s not _that_ easy to map the positions 🙃

Does anyone have any ideas what I can do differently or better?

---

<div class="post-metadata">

**Author:** ![marijn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/marijn/32/15_2.png) [@marijn](https://discuss.prosemirror.net/u/marijn)\
**Post date:** [May 15, 2021, 12:50pm UTC](https://discuss.prosemirror.net/t/how-to-calculate-the-changed-ranges-for-transactions/3771/2 "2021-05-15T12:50:58Z")

</div>

Depending on how precise you need these to be (and how heavily you use them) it may be worth pulling in [prosemirror-changeset](https://github.com/ProseMirror/prosemirror-changeset) as a dependency. Your code seems to mostly have the right idea but you’ll definitely want to `slice` from `index + 1` in `upcomingMapping` (`newStart` already points into the document _after_ the step). Maybe that’s what’s causing the problem?

---

<div class="post-metadata">

**Author:** ![philippkuehn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/philippkuehn/32/1820_2.png) [@philippkuehn](https://discuss.prosemirror.net/u/philippkuehn)\
**Post date:** [May 15, 2021, 1:34pm UTC](https://discuss.prosemirror.net/t/how-to-calculate-the-changed-ranges-for-transactions/3771/3 "2021-05-15T13:34:08Z")

</div>

> Your code seems to mostly have the right idea but you’ll definitely want to `slice` from `index + 1` in `upcomingMapping` (`newStart` already points into the document _after_ the step). Maybe that’s what’s causing the problem?

Yeah you are right, but in my example I’m always calculating based on `oldStart` and `oldEnd`.

So basically this:

```js
const upcomingMapping = transaction.mapping
    .slice(index, transaction.mapping.maps.length)

step.getMap().forEach((oldStart, oldEnd, newStart, newEnd) => {
  // using old values here
  const newStart = upcomingMapping.map(oldStart, -1)
  const newEnd = upcomingMapping.map(oldEnd)
})

```

Should be the same as this, right? (At least it makes no difference in my case.)

```js
const upcomingMapping = transaction.mapping
    .slice(index + 1, transaction.mapping.maps.length)

step.getMap().forEach((oldStart, oldEnd, newStart, newEnd) => {
  // using new values here
  const newStart = upcomingMapping.map(newStart, -1)
  const newEnd = upcomingMapping.map(newEnd)
})

```

But I have problems with getting the correct values for `oldEnd` when undoing changes. When starting with an empty document and typing one character I get these values (which are correct):

```json
{oldStart: 1, oldEnd: 1, newStart: 1, newEnd: 2}

```

When undoing these steps I get these values:

```json
{oldStart: 1, oldEnd: 3, newStart: 1, newEnd: 1}

```

I don’t get what I’m doing wrong in this most simple change like described above. Any idea?

---

<div class="post-metadata">

**Author:** ![philippkuehn](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/philippkuehn/32/1820_2.png) [@philippkuehn](https://discuss.prosemirror.net/u/philippkuehn)\
**Post date:** [May 15, 2021, 10:15pm UTC](https://discuss.prosemirror.net/t/how-to-calculate-the-changed-ranges-for-transactions/3771/4 "2021-05-15T22:15:25Z")

</div>

Got it!

```auto
transaction.mapping.maps.forEach((stepMap, index) => {
  stepMap.forEach((from, to) => {
    const newStart = transaction.mapping.slice(index).map(from, -1)
    const newEnd = transaction.mapping.slice(index).map(to)
    const oldStart = transaction.mapping.invert().map(newStart, -1)
    const oldEnd = transaction.mapping.invert().map(newEnd)
  })
})

```

---

<div class="post-metadata">

**Author:** ![annabialas](https://discuss.prosemirror.net/user_avatar/discuss.prosemirror.net/annabialas/32/4748_2.png) [@annabialas](https://discuss.prosemirror.net/u/annabialas)\
**Post date:** [October 24, 2025, 4:52am UTC](https://discuss.prosemirror.net/t/how-to-calculate-the-changed-ranges-for-transactions/3771/5 "2025-10-24T04:52:42Z")

</div>

couple years later i’m writing a lightweight truncation function and this saved me some time : - ) thanks!
