removeSteps API in prosemirror-changeset

For prosemirror-changeset, is there a way to implement removeSteps using the current API?

The use case would be removing a change if it has been ‘accepted’.

I’ve tried changeSet.addSteps(changeSet.startDoc, invertedMaps) but it only works when the changeSet only has a singular change.

I suspect the correct approach would be to apply steps associated with the change I want to ‘accept’ to startDoc, and then add all other maps, equivalent to a git rebase perhaps.

I don’t think implementing something like that would be possible, no. You’ll have to replay the changes as you describe.

Is there a reason why the constructor for ChangeSet isn’t public; wondering if this could also be valid if it was?

function acceptChange(change, changeSet) {
  const changes = changeSet.changes.filter(c => c !== change)
  return new ChangeSet(changeSet.config, changes) // constructor not public
}

There’s no guarantee that changed ranges can be reverted individually—for example if you wrap a paragraph in a list, that’ll result in changes before and after the paragraph, but you cannot revert just one of them. Similarly, filtering out a single changed range like that is not reliably going to give you a coherent change set.

1 Like

Is this because of simplyChanges, or just that transactions can contain multiple steps so a transaction is the atomic unit here for reverting or accepting steps / changes?

Looking at the track changes example with commits, how would you revert a single commit without reverting other changes? A commit just stores maps and steps, no transactions.

You just need steps and maps to revert changes. But change sets store changed ranges, not steps.

Would storing indexes of the steps that created a change work to map changedRanges back to steps in a robust way? My current approach is

      apply(tr, prevState) {
        if (!tr.docChanged) return prevState;
        let changeSet = prevState.changeSet;
        changeSet = changeSet.addSteps(tr.doc, tr.mapping.maps, tr.steps.map((step, i) => ({ index: prevState.steps.length + i })))
        const inverted = tr.steps.map((step, i) => step.invert(tr.docs[i]));
        return {
          changeSet,
          steps: prevState.steps.concat(inverted),
          maps: prevState.maps.concat(tr.mapping.maps),
        }
      },
    const stepIds: number[] = Array.from(new Set(change.inserted.concat(change.deleted).map(s => s.data.index))).sort((a, b) => a - b)
    const steps = stepIds.map(idx => diffState.steps[idx]) // Steps to rebase or revert / undo

Sure, you map those steps forward properly, they can be used to create a valid change relative to the current document.