Skip to content

Commit

Permalink
allow supplying document updates as snapshots
Browse files Browse the repository at this point in the history
  • Loading branch information
dmonad committed Jun 6, 2024
1 parent a5a921a commit dd6e496
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 9 deletions.
22 changes: 19 additions & 3 deletions src/plugins/sync-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -419,18 +419,34 @@ export class ProsemirrorBinding {
}

/**
* @param {Y.Snapshot} snapshot
* @param {Y.Snapshot} prevSnapshot
* @param {Y.Snapshot|Uint8Array} snapshot
* @param {Y.Snapshot|Uint8Array} prevSnapshot
* @param {Object} pluginState
*/
_renderSnapshot (snapshot, prevSnapshot, pluginState) {
/**
* The document that contains the full history of this document.
* @type {Y.Doc}
*/
let historyDoc = this.doc
if (!snapshot) {
snapshot = Y.snapshot(this.doc)
}
if (snapshot instanceof Uint8Array || prevSnapshot instanceof Uint8Array) {
if (!(snapshot instanceof Uint8Array) || !(prevSnapshot instanceof Uint8Array)) {
// expected both snapshots to be v2 updates
error.unexpectedCase()
}
historyDoc = new Y.Doc({ gc: false })
Y.applyUpdateV2(historyDoc, prevSnapshot)
prevSnapshot = Y.snapshot(historyDoc)
Y.applyUpdateV2(historyDoc, snapshot)
snapshot = Y.snapshot(historyDoc)
}
// clear mapping because we are going to rerender
this.mapping = new Map()
this.mux(() => {
this.doc.transact((transaction) => {
historyDoc.transact((transaction) => {
// before rendering, we are going to sanitize ops and split deleted ops
// if they were deleted by seperate users.
const pud = pluginState.permanentUserData
Expand Down
9 changes: 3 additions & 6 deletions tests/complexSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,19 +223,16 @@ export const marks = {
return codeDOM
}
},

ychange: {
attrs: {
user: { default: null },
state: { default: null }
type: { default: null }
},
inclusive: false,
parseDOM: [{ tag: 'ychange' }],
toDOM (node) {
return [
'ychange',
{ ychange_user: node.attrs.user, ychange_state: node.attrs.state },
0
]
return ['ychange', { ychange_user: node.attrs.user, ychange_type: node.attrs.type }]
}
}
}
Expand Down
46 changes: 46 additions & 0 deletions tests/y-prosemirror.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,52 @@ export const testInitialCursorPosition2 = async (_tc) => {
t.assert(view.state.selection.head === 0)
}

export const testVersioning = async (_tc) => {
const ydoc = new Y.Doc({ gc: false })
const yxml = ydoc.get('prosemirror', Y.XmlFragment)
const permanentUserData = new Y.PermanentUserData(ydoc)
permanentUserData.setUserMapping(ydoc, ydoc.clientID, 'me')
ydoc.gc = false
console.log('yxml', yxml.toString())
const view = createNewComplexProsemirrorView(ydoc)
const p = new Y.XmlElement('paragraph')
const ytext = new Y.XmlText('hello world!')
p.insert(0, [ytext])
yxml.insert(0, [p])
const snapshot1 = Y.snapshot(ydoc)
const snapshotDoc1 = Y.encodeStateAsUpdateV2(ydoc)
ytext.delete(0, 6)
const snapshot2 = Y.snapshot(ydoc)
const snapshotDoc2 = Y.encodeStateAsUpdateV2(ydoc)
view.dispatch(
view.state.tr.setMeta(ySyncPluginKey, { snapshot: snapshot2, prevSnapshot: snapshot1, permanentUserData })
)
await promise.wait(50)
console.log('calculated diff via snapshots: ', view.state.doc.toJSON())
// recreate the JSON, because ProseMirror messes with the constructors
const viewstate1 = JSON.parse(JSON.stringify(view.state.doc.toJSON().content[1].content))
const expectedState = [{
type: 'text',
marks: [{ type: 'ychange', attrs: { user: 'me', type: 'removed' } }],
text: 'hello '
}, {
type: 'text',
text: 'world!'
}]
console.log('calculated diff via snapshots: ', JSON.stringify(viewstate1))
t.compare(viewstate1, expectedState)

t.info('now check whether we get the same result when rendering the updates')
view.dispatch(
view.state.tr.setMeta(ySyncPluginKey, { snapshot: snapshotDoc2, prevSnapshot: snapshotDoc1, permanentUserData })
)
await promise.wait(50)

const viewstate2 = JSON.parse(JSON.stringify(view.state.doc.toJSON().content[1].content))
console.log('calculated diff via updates: ', JSON.stringify(viewstate2))
t.compare(viewstate2, expectedState)
}

export const testAddToHistoryIgnore = (_tc) => {
const ydoc = new Y.Doc()
const view = createNewProsemirrorViewWithUndoManager(ydoc)
Expand Down

0 comments on commit dd6e496

Please sign in to comment.