Identifying the changed nodes for a given transaction

I’m trying to add a checksum to each of my doc’s children nodes within a plugin. The problem that I am having is determining the changed nodes using a transaction. What I have set up currently is using a plugin with an appendTransaction and only computing a checksum when there exists a transaction with a docChanged.

Is there an incantation that you are aware of that can allow me to take a transaction and identify which specific node has changed?

Thank you for any insight or help!

You can find the ranges changed in a transaction by inspecting the ranges in the step maps, but you have to be careful to map them forward through subsequent steps if the transaction makes multiple steps, to get positions in the final document.

Thanks for your quick response!

I think I can manage the book keeping across each transaction using the forEach() method you linked. I don’t suppose there is a more straightforward way to do this within prosemirror?

I believe I have some solution which achieves my intended goal. I ended up using prosemirror-changeset/src/changeset.ts at 2.2.1 · ProseMirror/prosemirror-changeset · GitHub to do the book keeping for me. Now my plugin’s appendTransaction looks like this:

new Plugin({
	...
		appendTransaction: (transactions, prevState, nextState) => {
			const tr = nextState.tr;
			if (transactions.some((transaction) => transaction.docChanged)) {
				// Construct the ChangeSet from the list of transactions that changes the doc itself
				let changeSet: ChangeSet = ChangeSet.create(prevState.doc);
				for (const txn of transactions.filter((txn) => txn.docChanged)) {
					// StepMap: https://prosemirror.net/docs/ref/#transform.StepMap
					// ChangeSet: https://github.com/ProseMirror/prosemirror-changeset/blob/2.2.1/src/changeset.ts#L21-L31
					// Translate the transaction steps into an array of StepMaps and add them to the ChangeSet.
					changeSet = changeSet.addSteps(
						changeSet.startDoc,
						txn.steps.map((step) => step.getMap()),
						[]
					);
				}
				// The resulting ChangeSet contains the information of insertions/deletions for the transactions
				// Each change contains the oldStart/oldEnd + newStart/newEnd positions.
				for (const change of changeSet.changes) {
					// Identify the nodes which this change touches
					// Using the fromB and toB here since this is the resulting doc after the changes.
					const start = change.fromB;
					const end = change.toB;
					// Get the nodes between the change positions.
					nextState.doc.nodesBetween(start, end, (node, pos) => {
						// These are all the changed direct descendant nodes on the current document.
						return false;
					});
				}
			}
			return tr;
		}
}
1 Like