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