I’m reimplementing the track example to be more granular, so that individual changes can be reverted or accepted.
prosemirror-changeset seems like a wonderful utility for this, although I’m having trouble understanding the inserted and deleted API and how that interacts with the data field.
My approach currently is to invert the steps and pass their global index as data
A change can both delete content and insert content. Both of those may have data associated with them, and a given changed range may have different pieces with different data values in it. Could you say a bit more about why this is confusing?
That’s not the kind of thing prosemirror-changeset can help you with. That converts from steps to changed ranges. It sounds like you want to store the actual steps instead.
I am storing the steps, but wondering if there’s a better way of associating changes with a series of steps, and then accessing those series of steps so I can either “revert” the change by applying inverted steps, or “accept” the change by removing step from the changeset.
My current approach is to keep the steps separately, but store the index of the steps:
export function acceptChange(change: Change): Command {
return function (state, dispatch) {
const tr = state.tr;
const diffState = diffPluginKey.getState(state);
if (!diffState) return false;
const changeSet = diffState.changeSet as ChangeSet;
const updated = changeSet.changes.filter(c => c !== change);
// No API to removeSteps so we re-create with filtered changes.
tr.setMeta(diffPluginKey, new ChangeSet(changeSet.config, updated));
if (dispatch) dispatch(tr);
return true;
}
}
export function revertChange(change: Change): Command {
return function (state, dispatch) {
// De-duplicate indices as data can appear twice in deleted and inserted
const stepIds: number[] = Array.from(new Set(
change.inserted.map(span => span.data.index).concat(change.deleted.map(span => span.data.index))
))
const diffState = diffPluginKey.getState(state);
if (!diffState) return false;
const remap = new Mapping(diffState.maps)
const tr = state.tr;
// From tracking example:
// Build up a transaction that includes all (inverted) steps in this
// commit, rebased to the current document. They have to be applied
// in reverse order.
for (let i = stepIds.length - 1; i >= 0; i--) {
const index = stepIds[i];
const step = diffState.steps[index].map(remap.slice(index + 1))
if (!step) continue;
tr.step(step);
}
if (!tr.docChanged) return false;
if (dispatch) dispatch(tr);
return true;
}
}