refactor #538 (formatting attrs) a bit
This commit is contained in:
parent
08801dd406
commit
ce098d0ac2
@ -476,6 +476,56 @@ export const cleanupYTextFormatting = type => {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This will be called by the transction once the event handlers are called to potentially cleanup
|
||||||
|
* formatting attributes.
|
||||||
|
*
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
*/
|
||||||
|
export const cleanupYTextAfterTransaction = transaction => {
|
||||||
|
/**
|
||||||
|
* @type {Set<YText>}
|
||||||
|
*/
|
||||||
|
const needFullCleanup = new Set()
|
||||||
|
// check if another formatting item was inserted
|
||||||
|
const doc = transaction.doc
|
||||||
|
for (const [client, afterClock] of transaction.afterState.entries()) {
|
||||||
|
const clock = transaction.beforeState.get(client) || 0
|
||||||
|
if (afterClock === clock) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
iterateStructs(transaction, /** @type {Array<Item|GC>} */ (doc.store.clients.get(client)), clock, afterClock, item => {
|
||||||
|
if (
|
||||||
|
!item.deleted && /** @type {Item} */ (item).content.constructor === ContentFormat && item.constructor !== GC
|
||||||
|
) {
|
||||||
|
needFullCleanup.add(/** @type {any} */ (item).parent)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// cleanup in a new transaction
|
||||||
|
transact(doc, (t) => {
|
||||||
|
iterateDeletedStructs(transaction, transaction.deleteSet, item => {
|
||||||
|
if (item instanceof GC || needFullCleanup.has(/** @type {YText} */ (item.parent))) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const parent = /** @type {YText} */ (item.parent)
|
||||||
|
if (item.content.constructor === ContentFormat) {
|
||||||
|
needFullCleanup.add(parent)
|
||||||
|
} else {
|
||||||
|
// If no formatting attribute was inserted or deleted, we can make due with contextless
|
||||||
|
// formatting cleanups.
|
||||||
|
// Contextless: it is not necessary to compute currentAttributes for the affected position.
|
||||||
|
cleanupContextlessFormattingGap(t, item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// If a formatting item was inserted, we simply clean the whole type.
|
||||||
|
// We need to compute currentAttributes for the current position anyway.
|
||||||
|
for (const yText of needFullCleanup) {
|
||||||
|
cleanupYTextFormatting(yText)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {ItemTextListPosition} currPos
|
* @param {ItemTextListPosition} currPos
|
||||||
@ -862,59 +912,10 @@ export class YText extends AbstractType {
|
|||||||
callTypeObservers(this, transaction, event)
|
callTypeObservers(this, transaction, event)
|
||||||
// If a remote change happened, we try to cleanup potential formatting duplicates.
|
// If a remote change happened, we try to cleanup potential formatting duplicates.
|
||||||
if (!transaction.local) {
|
if (!transaction.local) {
|
||||||
transaction._yTexts.add(this)
|
transaction._needFormattingCleanup = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Transaction} transaction
|
|
||||||
*/
|
|
||||||
static _cleanup (transaction) {
|
|
||||||
const withFormattingItems = new Set()
|
|
||||||
// check if another formatting item was inserted
|
|
||||||
const doc = transaction.doc
|
|
||||||
for (const [client, afterClock] of transaction.afterState.entries()) {
|
|
||||||
const clock = transaction.beforeState.get(client) || 0
|
|
||||||
if (afterClock === clock) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
iterateStructs(transaction, /** @type {Array<Item|GC>} */ (doc.store.clients.get(client)), clock, afterClock, item => {
|
|
||||||
if (!item.deleted && /** @type {Item} */ (item).content.constructor === ContentFormat && !(item instanceof GC) && transaction._yTexts.has(/** @type YText */ (item.parent))) {
|
|
||||||
withFormattingItems.add(item.parent)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
iterateDeletedStructs(transaction, transaction.deleteSet, item => {
|
|
||||||
if (item instanceof GC) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (transaction._yTexts.has(/** @type YText */ (item.parent)) && item.content.constructor === ContentFormat) {
|
|
||||||
withFormattingItems.add(item.parent)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
transact(doc, (t) => {
|
|
||||||
for (const yText of transaction._yTexts) {
|
|
||||||
if (withFormattingItems.has(yText)) {
|
|
||||||
// If a formatting item was inserted, we simply clean the whole type.
|
|
||||||
// We need to compute currentAttributes for the current position anyway.
|
|
||||||
cleanupYTextFormatting(yText)
|
|
||||||
} else {
|
|
||||||
// If no formatting attribute was inserted, we can make due with contextless
|
|
||||||
// formatting cleanups.
|
|
||||||
// Contextless: it is not necessary to compute currentAttributes for the affected position.
|
|
||||||
iterateDeletedStructs(t, t.deleteSet, item => {
|
|
||||||
if (item instanceof GC) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (item.parent === yText) {
|
|
||||||
cleanupContextlessFormattingGap(t, item)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the unformatted string representation of this YText type.
|
* Returns the unformatted string representation of this YText type.
|
||||||
*
|
*
|
||||||
|
@ -11,7 +11,8 @@ import {
|
|||||||
Item,
|
Item,
|
||||||
generateNewClientId,
|
generateNewClientId,
|
||||||
createID,
|
createID,
|
||||||
UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc, YText // eslint-disable-line
|
cleanupYTextAfterTransaction,
|
||||||
|
UpdateEncoderV1, UpdateEncoderV2, GC, StructStore, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as map from 'lib0/map'
|
import * as map from 'lib0/map'
|
||||||
@ -115,9 +116,9 @@ export class Transaction {
|
|||||||
*/
|
*/
|
||||||
this.subdocsLoaded = new Set()
|
this.subdocsLoaded = new Set()
|
||||||
/**
|
/**
|
||||||
* @type {Set<YText>}
|
* @type {boolean}
|
||||||
*/
|
*/
|
||||||
this._yTexts = new Set()
|
this._needFormattingCleanup = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,10 +300,8 @@ const cleanupTransactions = (transactionCleanups, i) => {
|
|||||||
fs.push(() => doc.emit('afterTransaction', [transaction, doc]))
|
fs.push(() => doc.emit('afterTransaction', [transaction, doc]))
|
||||||
})
|
})
|
||||||
callAll(fs, [])
|
callAll(fs, [])
|
||||||
if (transaction._yTexts.size > 0) {
|
if (transaction._needFormattingCleanup) {
|
||||||
transact(doc, () => {
|
cleanupYTextAfterTransaction(transaction)
|
||||||
YText._cleanup(transaction)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
// Replace deleted items with ItemDeleted / GC.
|
// Replace deleted items with ItemDeleted / GC.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user