Would it be possible to have something akin to an amendTransaction hook, that would run just after filterTransaction? It’s come up quite a few times that I want to update a plugin state as an alternative to running a transaction. A couple of examples might be blocking a cursor movement (but remembering that it was blocked in some state), or deleting some content (and keeping it in the document for changeset reasons).

Alternatively is there a way of achieving this at the moment that I’ve missed?


1 Like

I guess we could have formulated filterTransaction as replaceTransaction instead, with the filter case just being a replacement with no transaction. If you want to start formulating an RFC on this (a replaceTransaction feature that can return an array of transactions, with filterTransaction reformulated to just be a backwards-compatibility shim on top of that) that might be interesting.

1 Like

I’ve had a bit of a think about this and I’ve realised it’s tricky implement … I feel like the most sensible approach is to allow plugins to take it in turns to replace / filter a transaction. When one filters it then act as before, but when one replaces it then this should again be run against all the other plugins to see whether they would go further a this point. This perhaps is a touch like the ability for appendTransaction to bounce into an infinite loop but it seems to rely more heavily on ordering than previously.

I’m not sure I quite understand why it would return an array here too?

I’ve done something like this outside of prosemirror before applying the new state

dispatchTransaction (tr) {
      if (this.interceptTransaction) tr = this.interceptTransaction(tr)
      if (!tr) return // leave unchanged
      this.editor.state = this.editor.state.apply(tr)

This sort of thing works ok at the editor level but you don’t have access to this at the plugin level and we’re looking to share the logic between a few editors.

yea the above pattern can definitely get messy too. I’d be ideal to contain the code in its own plugin.

when I was using vue.js the code was actually part of an editor package I used

1 Like

I’ve created an RFC for replaceTransaction that aims to solve this use-case. See, I’d appreciate any feedback on it.


Currently, we are using appendTransaction to fix documents that are correct by schema but by structure is not correct. For example, a table 5x5 where after a transaction is applied the final document doesn’t contain the correct number of cell/rows.

appendTransaction works perfect locally, but we run in a race condition once we add collab into the formula. Imagine two users, where the user A is trying to add a column and the user B is trying to add a row (at the same time).

This will cause the client B to receive a transaction where we are adding N cells (adding a column), but user B has a table of N+1 (user A table + the local row). We are going to attempt to fix the table using an append transaction and this new transaction is sent to the user A. At this point, an infinite bucle starts where each client is trying to fix the missing cell.

After further investigation, we got into the conclusion that we should not create the invalid document in the first place, so we need a way to fix the transaction before affecting the final state. We have a partial solution where we are amending the transaction in our custom dispatch, something like this:

new EditorView(..., {
  dispatchTransaction: (tr: transaction) => {
    amendTransaction(tr, state);
    dispatchTransation(tr, state);

amendTransaction is just a function that goes for each plugin looking for amendTransaction and execute this function in the transaction, mutating the existing transaction with the correct steps to fix the final document.

Do you think we could do something different or is this something that can be added to the core?

See this discussion for reasons why it’s not so simple to do this.

Are you sending transactions to the server separately, in your collab setup? Typically, all steps for a group of appended transactions would be sent over in one batch, so other clients don’t see the invalid state.