fix gc when item is deleted in observer call
This commit is contained in:
parent
a336cc167c
commit
8c5a06bbf8
@ -76,11 +76,17 @@
|
|||||||
img[ychange_state='removed'] {
|
img[ychange_state='removed'] {
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
.y-connect-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<p>This example shows how to bind a YXmlFragment type to a <a href="http://prosemirror.net">Prosemirror</a> editor.</p>
|
<button type="button" class="y-connect-btn">Disconnect</button>
|
||||||
<p>The content of this editor is shared with every client who visits this domain.</p>
|
<p>This example shows how to bind a YXmlFragment to a <a href="http://prosemirror.net">Prosemirror</a> editor using <a href="https://github.com/y-js/y-prosemirror">y-prosemirror</a>.</p>
|
||||||
|
<p>The content of this editor is shared with every client that visits this domain.</p>
|
||||||
<div class="code-html">
|
<div class="code-html">
|
||||||
|
|
||||||
<div id="editor" style="margin-bottom: 23px"></div>
|
<div id="editor" style="margin-bottom: 23px"></div>
|
||||||
|
@ -22,4 +22,15 @@ const prosemirrorView = new EditorView(document.querySelector('#editor'), {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const connectBtn = document.querySelector('.y-connect-btn')
|
||||||
|
connectBtn.addEventListener('click', () => {
|
||||||
|
if (ydocument.wsconnected) {
|
||||||
|
ydocument.disconnect()
|
||||||
|
connectBtn.textContent = 'Connect'
|
||||||
|
} else {
|
||||||
|
ydocument.connect()
|
||||||
|
connectBtn.textContent = 'Disconnect'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
window.example = { provider, ydocument, type, prosemirrorView }
|
window.example = { provider, ydocument, type, prosemirrorView }
|
||||||
|
12
package-lock.json
generated
12
package-lock.json
generated
@ -3036,9 +3036,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lib0": {
|
"lib0": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.0.2.tgz",
|
||||||
"integrity": "sha512-k2BL//TczDqrl+GzTp8vUkZdyO/tJaMkN3O0OcqmfROvsmmBOYvdEGuwLacLl+c7AB6qFwmdh3cdgqg6YVPn2Q=="
|
"integrity": "sha512-7bJgV2emHGRO5kpj66Gdc9SQKVfhDBLx0UIS/aU5P8R0179nRFHKDTYGvLlNloWbeUUARlqk3ndFIO4JbUy7Sw=="
|
||||||
},
|
},
|
||||||
"live-server": {
|
"live-server": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
@ -5578,6 +5578,12 @@
|
|||||||
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
|
"integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"y-protocols": {
|
||||||
|
"version": "0.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-0.0.2.tgz",
|
||||||
|
"integrity": "sha512-ixAaywK7USrMX7h6H2PZ59rtNVZcfJCNO0+/gDhAV1Sizwxdt0T6wPm9RCxDJtY3pXNeWA8MgtBysMGkgGA5xA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"yallist": {
|
"yallist": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||||
|
@ -55,9 +55,10 @@
|
|||||||
},
|
},
|
||||||
"homepage": "http://y-js.org",
|
"homepage": "http://y-js.org",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lib0": "0.0.1"
|
"lib0": "0.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"y-protocols": "0.0.2",
|
||||||
"codemirror": "^5.42.0",
|
"codemirror": "^5.42.0",
|
||||||
"concurrently": "^3.6.1",
|
"concurrently": "^3.6.1",
|
||||||
"jsdoc": "^3.5.5",
|
"jsdoc": "^3.5.5",
|
||||||
|
@ -421,13 +421,14 @@ export class AbstractItem extends AbstractStruct {
|
|||||||
/**
|
/**
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
|
* @param {boolean} parentGCd
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
gc (transaction, store) {
|
gc (transaction, store, parentGCd) {
|
||||||
this.delete(transaction)
|
this.delete(transaction)
|
||||||
let r
|
let r
|
||||||
if (this.parent._item !== null && this.parent._item.deleted) {
|
if (parentGCd) {
|
||||||
r = new GC(this.id, this.length)
|
r = new GC(this.id, this.length)
|
||||||
} else {
|
} else {
|
||||||
r = new ItemDeleted(this.id, this.left, this.origin, this.right, this.rightOrigin, this.parent, this.parentSub, this.length)
|
r = new ItemDeleted(this.id, this.left, this.origin, this.right, this.rightOrigin, this.parent, this.parentSub, this.length)
|
||||||
|
@ -85,6 +85,19 @@ export class ItemDeleted extends AbstractItem {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Transaction} transaction
|
||||||
|
* @param {StructStore} store
|
||||||
|
* @param {boolean} parentGCd
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
gc (transaction, store, parentGCd) {
|
||||||
|
if (parentGCd) {
|
||||||
|
super.gc(transaction, store, parentGCd)
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @param {encoding.Encoder} encoder
|
* @param {encoding.Encoder} encoder
|
||||||
* @param {number} offset
|
* @param {number} offset
|
||||||
|
@ -124,13 +124,13 @@ export class ItemType extends AbstractItem {
|
|||||||
gcChildren (transaction, store) {
|
gcChildren (transaction, store) {
|
||||||
let item = this.type._start
|
let item = this.type._start
|
||||||
while (item !== null) {
|
while (item !== null) {
|
||||||
item.gc(transaction, store)
|
item.gc(transaction, store, true)
|
||||||
item = item.right
|
item = item.right
|
||||||
}
|
}
|
||||||
this.type._start = null
|
this.type._start = null
|
||||||
this.type._map.forEach(item => {
|
this.type._map.forEach(item => {
|
||||||
while (item !== null) {
|
while (item !== null) {
|
||||||
item.gc(transaction, store)
|
item.gc(transaction, store, true)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
item = item.left
|
item = item.left
|
||||||
}
|
}
|
||||||
@ -141,10 +141,11 @@ export class ItemType extends AbstractItem {
|
|||||||
/**
|
/**
|
||||||
* @param {Transaction} transaction
|
* @param {Transaction} transaction
|
||||||
* @param {StructStore} store
|
* @param {StructStore} store
|
||||||
|
* @param {boolean} parentGCd
|
||||||
*/
|
*/
|
||||||
gc (transaction, store) {
|
gc (transaction, store, parentGCd) {
|
||||||
this.gcChildren(transaction, store)
|
this.gcChildren(transaction, store)
|
||||||
super.gc(transaction, store)
|
super.gc(transaction, store, parentGCd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ import {
|
|||||||
findIndexSS,
|
findIndexSS,
|
||||||
callEventHandlerListeners,
|
callEventHandlerListeners,
|
||||||
AbstractItem,
|
AbstractItem,
|
||||||
ItemDeleted,
|
|
||||||
ID, AbstractType, AbstractStruct, YEvent, Y // eslint-disable-line
|
ID, AbstractType, AbstractStruct, YEvent, Y // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
@ -45,8 +44,9 @@ import * as math from 'lib0/math.js'
|
|||||||
export class Transaction {
|
export class Transaction {
|
||||||
/**
|
/**
|
||||||
* @param {Y} y
|
* @param {Y} y
|
||||||
|
* @param {any} origin
|
||||||
*/
|
*/
|
||||||
constructor (y) {
|
constructor (y, origin) {
|
||||||
/**
|
/**
|
||||||
* The Yjs instance.
|
* The Yjs instance.
|
||||||
* @type {Y}
|
* @type {Y}
|
||||||
@ -90,6 +90,10 @@ export class Transaction {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this._mergeStructs = new Set()
|
this._mergeStructs = new Set()
|
||||||
|
/**
|
||||||
|
* @type {any}
|
||||||
|
*/
|
||||||
|
this.origin = origin
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @type {encoding.Encoder|null}
|
* @type {encoding.Encoder|null}
|
||||||
@ -124,22 +128,24 @@ export const nextID = transaction => {
|
|||||||
*
|
*
|
||||||
* @param {Y} y
|
* @param {Y} y
|
||||||
* @param {function(Transaction):void} f
|
* @param {function(Transaction):void} f
|
||||||
|
* @param {any} [origin]
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const transact = (y, f) => {
|
export const transact = (y, f, origin = null) => {
|
||||||
const transactionCleanups = y._transactionCleanups
|
const transactionCleanups = y._transactionCleanups
|
||||||
let initialCall = false
|
let initialCall = false
|
||||||
if (y._transaction === null) {
|
if (y._transaction === null) {
|
||||||
initialCall = true
|
initialCall = true
|
||||||
y._transaction = new Transaction(y)
|
y._transaction = new Transaction(y, origin)
|
||||||
transactionCleanups.push(y._transaction)
|
transactionCleanups.push(y._transaction)
|
||||||
y.emit('beforeTransaction', [y._transaction, y])
|
y.emit('beforeTransaction', [y._transaction, y])
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
f(y._transaction)
|
f(y._transaction)
|
||||||
} finally {
|
} finally {
|
||||||
|
// @todo set after state here
|
||||||
if (initialCall && transactionCleanups[0] === y._transaction) {
|
if (initialCall && transactionCleanups[0] === y._transaction) {
|
||||||
// The first transaction ended, now process observer calls.
|
// 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.
|
// Observer call may create new transactions for which we need to call the observers and do cleanup.
|
||||||
@ -185,13 +191,7 @@ export const transact = (y, f) => {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
if (struct.deleted && struct instanceof AbstractItem) {
|
if (struct.deleted && struct instanceof AbstractItem) {
|
||||||
if (struct.constructor !== ItemDeleted || (struct.parent._item !== null && struct.parent._item.deleted)) {
|
struct.gc(transaction, store, false)
|
||||||
// check if we can GC
|
|
||||||
struct.gc(transaction, store)
|
|
||||||
} else {
|
|
||||||
// otherwise only gc children (if there are any)
|
|
||||||
struct.gcChildren(transaction, store)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,11 +52,12 @@ export class Y extends Observable {
|
|||||||
* other peers.
|
* other peers.
|
||||||
*
|
*
|
||||||
* @param {function(Transaction):void} f The function that should be executed as a transaction
|
* @param {function(Transaction):void} f The function that should be executed as a transaction
|
||||||
|
* @param {any} [origin] Origin of who started the transaction. Will be stored on transaction.origin
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
transact (f) {
|
transact (f, origin = null) {
|
||||||
transact(this, f)
|
transact(this, f, origin)
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Define a shared data type.
|
* Define a shared data type.
|
||||||
|
@ -19,14 +19,14 @@ import * as syncProtocol from 'y-protocols/sync.js'
|
|||||||
* @param {TestYInstance} y
|
* @param {TestYInstance} y
|
||||||
*/
|
*/
|
||||||
const afterTransaction = (transaction, y) => {
|
const afterTransaction = (transaction, y) => {
|
||||||
y.mMux(() => {
|
if (transaction.origin !== y.tc) {
|
||||||
const m = transaction.updateMessage
|
const m = transaction.updateMessage
|
||||||
if (m !== null) {
|
if (m !== null) {
|
||||||
const encoder = encoding.createEncoder()
|
const encoder = encoding.createEncoder()
|
||||||
syncProtocol.writeUpdate(encoder, m)
|
syncProtocol.writeUpdate(encoder, m)
|
||||||
broadcastMessage(y, encoding.toBuffer(encoder))
|
broadcastMessage(y, encoding.toBuffer(encoder))
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,11 +59,6 @@ export class TestYInstance extends Y.Y {
|
|||||||
* @type {Map<TestYInstance, Array<ArrayBuffer>>}
|
* @type {Map<TestYInstance, Array<ArrayBuffer>>}
|
||||||
*/
|
*/
|
||||||
this.receiving = new Map()
|
this.receiving = new Map()
|
||||||
/**
|
|
||||||
* Message mutex
|
|
||||||
* @type {Function}
|
|
||||||
*/
|
|
||||||
this.mMux = createMutex()
|
|
||||||
testConnector.allConns.add(this)
|
testConnector.allConns.add(this)
|
||||||
// set up observe on local model
|
// set up observe on local model
|
||||||
this.on('afterTransactionCleanup', afterTransaction)
|
this.on('afterTransactionCleanup', afterTransaction)
|
||||||
@ -165,11 +160,9 @@ export class TestConnector {
|
|||||||
return this.flushRandomMessage()
|
return this.flushRandomMessage()
|
||||||
}
|
}
|
||||||
const encoder = encoding.createEncoder()
|
const encoder = encoding.createEncoder()
|
||||||
receiver.mMux(() => {
|
// console.log('receive (' + sender.userID + '->' + receiver.userID + '):\n', syncProtocol.stringifySyncMessage(decoding.createDecoder(m), receiver))
|
||||||
// console.log('receive (' + sender.userID + '->' + receiver.userID + '):\n', syncProtocol.stringifySyncMessage(decoding.createDecoder(m), receiver))
|
// do not publish data created when this function is executed (could be ss2 or update message)
|
||||||
// do not publish data created when this function is executed (could be ss2 or update message)
|
syncProtocol.readSyncMessage(decoding.createDecoder(m), encoder, receiver, receiver.tc)
|
||||||
syncProtocol.readSyncMessage(decoding.createDecoder(m), encoder, receiver)
|
|
||||||
})
|
|
||||||
if (encoding.length(encoder) > 0) {
|
if (encoding.length(encoder) > 0) {
|
||||||
// send reply message
|
// send reply message
|
||||||
sender._receive(encoding.toBuffer(encoder), receiver)
|
sender._receive(encoding.toBuffer(encoder), receiver)
|
||||||
|
@ -54,9 +54,9 @@
|
|||||||
/* Experimental Options */
|
/* Experimental Options */
|
||||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||||
"maxNodeModuleJsDepth": 5,
|
"maxNodeModuleJsDepth": 0,
|
||||||
// "types": ["./src/utils/typedefs.js"]
|
// "types": ["./src/utils/typedefs.js"]
|
||||||
},
|
},
|
||||||
"include": ["./src/**/*", "./tests/**/*"],
|
"include": ["./src/**/*", "./tests/**/*"],
|
||||||
"exclude": ["../lib0/**/*", "node_modules"]
|
"exclude": ["../lib0/**/*", "node_modules/**/*", "dist", "dist/**/*.js"]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user