correctly handle gc with UndoManager and un-merge when syncing
This commit is contained in:
parent
ef6eb08335
commit
94933a704d
@ -19,8 +19,7 @@ export default {
|
||||
browser: true
|
||||
}),
|
||||
commonjs(),
|
||||
// babel(),
|
||||
/*
|
||||
babel(),
|
||||
uglify({
|
||||
mangle: {
|
||||
except: ['YMap', 'Y', 'YArray', 'YText', 'YXmlHook', 'YXmlFragment', 'YXmlElement', 'YXmlEvent', 'YXmlText', 'YEvent', 'YArrayEvent', 'YMapEvent', 'Type', 'Delete', 'ItemJSON', 'ItemString', 'Item']
|
||||
@ -36,7 +35,6 @@ export default {
|
||||
}
|
||||
}
|
||||
})
|
||||
*/
|
||||
],
|
||||
banner: `
|
||||
/**
|
||||
|
@ -92,7 +92,7 @@ export function readDeleteSet (y, decoder) {
|
||||
// delete maximum the len of d
|
||||
// else delete as much as possible
|
||||
diff = Math.min(n._id.clock - d[0], d[1])
|
||||
// deleteItemRange(y, user, d[0], diff)
|
||||
// deleteItemRange(y, user, d[0], diff, true)
|
||||
deletions.push([user, d[0], diff])
|
||||
} else {
|
||||
// 3)
|
||||
@ -100,7 +100,7 @@ export function readDeleteSet (y, decoder) {
|
||||
if (d[2] && !n.gc) {
|
||||
// d marks as gc'd but n does not
|
||||
// then delete either way
|
||||
// deleteItemRange(y, user, d[0], Math.min(diff, d[1]))
|
||||
// deleteItemRange(y, user, d[0], Math.min(diff, d[1]), true)
|
||||
deletions.push([user, d[0], Math.min(diff, d[1])])
|
||||
}
|
||||
}
|
||||
@ -117,12 +117,12 @@ export function readDeleteSet (y, decoder) {
|
||||
// Adapt the Tree implementation to support delete while iterating
|
||||
for (let i = deletions.length - 1; i >= 0; i--) {
|
||||
const del = deletions[i]
|
||||
deleteItemRange(y, del[0], del[1], del[2])
|
||||
deleteItemRange(y, del[0], del[1], del[2], true)
|
||||
}
|
||||
// for the rest.. just apply it
|
||||
for (; pos < dv.length; pos++) {
|
||||
d = dv[pos]
|
||||
deleteItemRange(y, user, d[0], d[1])
|
||||
deleteItemRange(y, user, d[0], d[1], true)
|
||||
// deletions.push([user, d[0], d[1], d[2]])
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,15 @@ export function writeStructs (y, encoder, ss) {
|
||||
for (let user of y.ss.state.keys()) {
|
||||
let clock = ss.get(user) || 0
|
||||
if (user !== RootFakeUserID) {
|
||||
y.os.iterate(new ID(user, clock), new ID(user, Number.MAX_VALUE), function (struct) {
|
||||
const minBound = new ID(user, clock)
|
||||
const overlappingLeft = y.os.findPrev(minBound)
|
||||
const rightID = overlappingLeft === null ? null : overlappingLeft._id
|
||||
if (rightID !== null && rightID.user === user && rightID.clock + overlappingLeft._length > clock) {
|
||||
const struct = overlappingLeft._clonePartial(clock - rightID.clock)
|
||||
struct._toBinary(encoder)
|
||||
len++
|
||||
}
|
||||
y.os.iterate(minBound, new ID(user, Number.MAX_VALUE), function (struct) {
|
||||
struct._toBinary(encoder)
|
||||
len++
|
||||
})
|
||||
|
@ -1,3 +1,4 @@
|
||||
|
||||
import Tree from '../Util/Tree.js'
|
||||
import ID from '../Util/ID/ID.js'
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { logID } from '../MessageHandler/messageToString.js'
|
||||
* Delete all items in an ID-range
|
||||
* TODO: implement getItemCleanStartNode for better performance (only one lookup)
|
||||
*/
|
||||
export function deleteItemRange (y, user, clock, range) {
|
||||
export function deleteItemRange (y, user, clock, range, gcChildren) {
|
||||
const createDelete = y.connector !== null && y.connector._forwardAppliedStructs
|
||||
let item = y.os.getItemCleanStart(new ID(user, clock))
|
||||
if (item !== null) {
|
||||
@ -24,7 +24,7 @@ export function deleteItemRange (y, user, clock, range) {
|
||||
const nodeVal = node.val
|
||||
if (!nodeVal._deleted) {
|
||||
nodeVal._splitAt(y, range)
|
||||
nodeVal._delete(y, createDelete, true)
|
||||
nodeVal._delete(y, createDelete, gcChildren)
|
||||
}
|
||||
const nodeLen = nodeVal._length
|
||||
range -= nodeLen
|
||||
@ -100,7 +100,7 @@ export default class Delete {
|
||||
if (!locallyCreated) {
|
||||
// from remote
|
||||
const id = this._targetID
|
||||
deleteItemRange(y, id.user, id.clock, this._length)
|
||||
deleteItemRange(y, id.user, id.clock, this._length, false)
|
||||
} else if (y.connector !== null) {
|
||||
// from local
|
||||
y.connector.broadcastStruct(this)
|
||||
|
@ -36,7 +36,6 @@ export default class GC {
|
||||
n._length += next._length
|
||||
y.os.delete(next._id)
|
||||
}
|
||||
|
||||
if (id.user !== RootFakeUserID) {
|
||||
if (y.connector !== null && (y.connector._forwardAppliedStructs || id.user === y.userID)) {
|
||||
y.connector.broadcastStruct(this)
|
||||
@ -85,4 +84,11 @@ export default class GC {
|
||||
_splitAt () {
|
||||
return this
|
||||
}
|
||||
|
||||
_clonePartial (diff) {
|
||||
const gc = new GC()
|
||||
gc._id = new ID(this._id.user, this._id.clock + diff)
|
||||
gc._length = this._length - diff
|
||||
return gc
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { getStructReference } from '../Util/structReferences.js'
|
||||
import ID from '../Util/ID/ID.js'
|
||||
import { RootFakeUserID } from '../Util/ID/RootID.js'
|
||||
import { default as RootID, RootFakeUserID } from '../Util/ID/RootID.js'
|
||||
import Delete from './Delete.js'
|
||||
import { transactionTypeChanged } from '../Transaction.js'
|
||||
import GC from './GC.js'
|
||||
@ -486,7 +486,12 @@ export default class Item {
|
||||
const parentID = decoder.readID()
|
||||
// parent does not change, so we don't have to search for it again
|
||||
if (this._parent === null) {
|
||||
const parent = y.os.get(parentID)
|
||||
let parent
|
||||
if (parentID.constructor === RootID) {
|
||||
parent = y.os.get(parentID)
|
||||
} else {
|
||||
parent = y.os.getItem(parentID)
|
||||
}
|
||||
if (parent === null) {
|
||||
missing.push(parentID)
|
||||
} else {
|
||||
|
@ -214,7 +214,10 @@ export default class Type extends Item {
|
||||
* @param {boolean} createDelete Whether to propagate a message that this
|
||||
* Type was deleted.
|
||||
*/
|
||||
_delete (y, createDelete, gcChildren = true) {
|
||||
_delete (y, createDelete, gcChildren) {
|
||||
if (gcChildren === undefined) {
|
||||
gcChildren = y._hasUndoManager === false
|
||||
}
|
||||
super._delete(y, createDelete, gcChildren)
|
||||
y._transaction.changedTypes.delete(this)
|
||||
// delete map types
|
||||
|
@ -75,6 +75,7 @@ export default class UndoManager {
|
||||
this._lastTransactionWasUndo = false
|
||||
const y = scope._y
|
||||
this.y = y
|
||||
y._hasUndoManager = true
|
||||
y.on('afterTransaction', (y, transaction, remote) => {
|
||||
if (!remote && transaction.changedParentTypes.has(scope)) {
|
||||
let reverseOperation = new ReverseOperation(y, transaction)
|
||||
|
Loading…
x
Reference in New Issue
Block a user