From 3ece681758cb7864264c23eb6a13f9ef68de3609 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Wed, 30 Nov 2022 12:09:19 +0100 Subject: [PATCH] allow transactions within event handlers having different origins --- src/types/YText.js | 2 +- src/utils/Transaction.js | 25 ++++++++++++++----------- tests/doc.tests.js | 25 +++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/types/YText.js b/src/types/YText.js index 896fcdd4..887eb6cb 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -1064,7 +1064,7 @@ export class YText extends AbstractType { n = n.right } packStr() - }, splitSnapshotAffectedStructs) + }, 'cleanup') return ops } diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index a933056c..f6a1179a 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -251,7 +251,6 @@ const cleanupTransactions = (transactionCleanups, i) => { try { sortAndMergeDeleteSet(ds) transaction.afterState = getStateVector(transaction.doc.store) - doc._transaction = null doc.emit('beforeObserverCalls', [transaction, doc]) /** * An array of event callbacks. @@ -398,16 +397,20 @@ export const transact = (doc, f, origin = null, local = true) => { try { f(doc._transaction) } finally { - if (initialCall && transactionCleanups[0] === doc._transaction) { - // The first transaction ended, now process observer calls. - // Observer call may create new transactions for which we need to call the observers and do cleanup. - // We don't want to nest these calls, so we execute these calls one after - // another. - // Also we need to ensure that all cleanups are called, even if the - // observes throw errors. - // This file is full of hacky try {} finally {} blocks to ensure that an - // event can throw errors and also that the cleanup is called. - cleanupTransactions(transactionCleanups, 0) + if (initialCall) { + const finishCleanup = doc._transaction === transactionCleanups[0] + doc._transaction = null + if (finishCleanup) { + // The first transaction ended, now process observer calls. + // Observer call may create new transactions for which we need to call the observers and do cleanup. + // We don't want to nest these calls, so we execute these calls one after + // another. + // Also we need to ensure that all cleanups are called, even if the + // observes throw errors. + // This file is full of hacky try {} finally {} blocks to ensure that an + // event can throw errors and also that the cleanup is called. + cleanupTransactions(transactionCleanups, 0) + } } } } diff --git a/tests/doc.tests.js b/tests/doc.tests.js index 994ecaeb..72495872 100644 --- a/tests/doc.tests.js +++ b/tests/doc.tests.js @@ -2,6 +2,31 @@ import * as Y from '../src/index.js' import * as t from 'lib0/testing' +/** + * @param {t.TestCase} _tc + */ +export const testOriginInTransaction = _tc => { + const doc = new Y.Doc() + const ytext = doc.getText() + /** + * @type {Array} + */ + const origins = [] + doc.on('afterTransaction', (tr) => { + origins.push(tr.origin) + if (origins.length <= 1) { + ytext.toDelta() + doc.transact(() => { + ytext.insert(0, 'a') + }, 'nested') + } + }) + doc.transact(() => { + ytext.insert(0, '0') + }, 'first') + t.compareArrays(origins, ['first', 'cleanup', 'nested']) +} + /** * Client id should be changed when an instance receives updates from another client using the same client id. *