This commit is contained in:
Ulion 2021-02-09 02:42:44 +00:00 committed by GitHub
commit 283b84ca8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 91 additions and 38 deletions

View File

@ -19,10 +19,12 @@ const byAction = {
const rootKey = '00000000-0000-0000-0000-000000000000' const rootKey = '00000000-0000-0000-0000-000000000000'
const toSlateOp = (ops: Automerge.Diff[], doc: SyncDoc) => { const toSlateOp = (ops: Automerge.Diff[], doc: SyncDoc) => {
const tempDoc = toJS(doc)
const iterate = (acc: [any, any[]], op: Automerge.Diff): any => { const iterate = (acc: [any, any[]], op: Automerge.Diff): any => {
const action = byAction[op.action] const action = byAction[op.action]
const result = action ? action(op, acc, doc) : acc const result = action ? action(op, acc, doc, tempDoc) : acc
return result return result
} }
@ -34,9 +36,7 @@ const toSlateOp = (ops: Automerge.Diff[], doc: SyncDoc) => {
[] []
]) ])
const tempDoc = toJS(doc) return defer.flatMap(op => op).filter(op => op)
return defer.flatMap(op => op(tempTree, tempDoc)).filter(op => op)
} }
export { toSlateOp } export { toSlateOp }

View File

@ -1,25 +1,35 @@
import * as Automerge from 'automerge' import * as Automerge from 'automerge'
import { Element, Node } from 'slate'
import { toSlatePath, toJS } from '../utils' import { toSlatePath, toJS } from '../utils'
import { SyncDoc } from '../model' import { SyncDoc } from '../model'
const insertTextOp = ({ index, path, value }: Automerge.Diff) => () => ({ const insertTextOp = ({ index, path, value }: Automerge.Diff) => (
type: 'insert_text', map: any,
path: toSlatePath(path), doc: Element
offset: index, ) => {
text: value, const slatePath = toSlatePath(path)
marks: [] const node = Node.get(doc, slatePath)!
}) const text = node.text! as string
node.text = [text.slice(0, index), value, text.slice(index)].join('')
return {
type: 'insert_text',
path: slatePath,
offset: index,
text: value,
marks: []
}
}
const insertNodeOp = ( const insertNodeOp = (
{ value, obj, index, path }: Automerge.Diff, { value, obj, index, path }: Automerge.Diff,
doc: any doc: any
) => (map: any) => { ) => (map: any, tmpDoc: Element) => {
const ops: any = [] const ops: any = []
const iterate = ({ children, ...json }: any, path: any) => { const iterate = ({ children, ...json }: any, path: any) => {
const node = children ? { ...json, children: [] } : json const node = toJS(children ? { ...json, children: [] } : json)
ops.push({ ops.push({
type: 'insert_node', type: 'insert_node',
@ -27,6 +37,11 @@ const insertNodeOp = (
node node
}) })
// update the temp doc so later remove_node won't error.
const parent = Node.parent(tmpDoc, path)
const index = path[path.length - 1]
parent.children.splice(index, 0, toJS(node))
children && children &&
children.forEach((n: any, i: any) => { children.forEach((n: any, i: any) => {
const node = map[n] || Automerge.getObjectById(doc, n) const node = map[n] || Automerge.getObjectById(doc, n)
@ -48,7 +63,12 @@ const insertByType = {
list: insertNodeOp list: insertNodeOp
} }
const opInsert = (op: Automerge.Diff, [map, ops]: any, doc: SyncDoc) => { const opInsert = (
op: Automerge.Diff,
[map, ops]: any,
doc: SyncDoc,
tmpDoc: Element
) => {
try { try {
const { link, obj, path, index, type, value } = op const { link, obj, path, index, type, value } = op
@ -61,10 +81,11 @@ const opInsert = (op: Automerge.Diff, [map, ops]: any, doc: SyncDoc) => {
.concat(value) .concat(value)
.concat(map[obj].slice(index)) .concat(map[obj].slice(index))
: value : value
} else { }
if (path) {
const insert = insertByType[type] const insert = insertByType[type]
const operation = insert && insert(op, doc) const operation = insert && insert(op, doc)(map, tmpDoc)
ops.push(operation) ops.push(operation)
} }

View File

@ -3,6 +3,7 @@ import { Element } from 'slate'
import { toSlatePath, toJS } from '../utils' import { toSlatePath, toJS } from '../utils'
import { getTarget } from '../path' import { getTarget } from '../path'
import { setDataOp } from './set'
const removeTextOp = (op: Automerge.Diff) => (map: any, doc: Element) => { const removeTextOp = (op: Automerge.Diff) => (map: any, doc: Element) => {
try { try {
@ -23,7 +24,9 @@ const removeTextOp = (op: Automerge.Diff) => (map: any, doc: Element) => {
const text = node?.text?.[index] || '*' const text = node?.text?.[index] || '*'
if (node) { if (node) {
node.text = node?.text ? (node.text.slice(0, index) + node.text.slice(index + 1)) : '' node.text = node?.text
? node.text.slice(0, index) + node.text.slice(index + 1)
: ''
} }
return { return {
@ -38,30 +41,32 @@ const removeTextOp = (op: Automerge.Diff) => (map: any, doc: Element) => {
} }
} }
const removeNodeOp = (op: Automerge.Diff) => ( const removeNodeOp = (op: Automerge.Diff) => (map: any, doc: Element) => {
map: any,
doc: Element
) => {
try { try {
const { index, obj, path } = op const { index, obj, path } = op
const slatePath = toSlatePath(path) const slatePath = toSlatePath(path)
const parent = getTarget(doc, slatePath) const parent = getTarget(doc, slatePath)
const target = parent?.children?.[index as number] || parent?.[index as number] || { children: [] } const target = parent?.children?.[index as number] ||
parent?.[index as number] || { children: [] }
if (!target) { if (!target) {
throw new TypeError('Target is not found!') throw new TypeError('Target is not found!')
} }
if (!map.hasOwnProperty(obj)) {
map[obj] = target
}
if (!Number.isInteger(index)) { if (!Number.isInteger(index)) {
throw new TypeError('Index is not a number') throw new TypeError('Index is not a number')
} }
if (parent?.children?.[index as number]) {
parent.children.splice(index, 1)
map[obj] = parent?.children
} else if (parent?.[index as number]) {
parent.splice(index, 1)
map[obj] = parent
}
return { return {
type: 'remove_node', type: 'remove_node',
path: slatePath.length ? slatePath.concat(index) : [index], path: slatePath.length ? slatePath.concat(index) : [index],
@ -72,10 +77,23 @@ const removeNodeOp = (op: Automerge.Diff) => (
} }
} }
const opRemove = (op: Automerge.Diff, [map, ops]: any) => { const opRemove = (
op: Automerge.Diff,
[map, ops]: any,
doc: any,
tmpDoc: Element
) => {
try { try {
const { index, path, obj, type } = op const { index, path, obj, type } = op
if (type === 'map' && path) {
// remove a key from map, mapping to slate set a key's value to undefined.
if (path[0] === 'children') {
ops.push(setDataOp(op, doc)(map, tmpDoc))
}
return [map, ops]
}
if ( if (
map.hasOwnProperty(obj) && map.hasOwnProperty(obj) &&
typeof map[obj] !== 'string' && typeof map[obj] !== 'string' &&
@ -91,11 +109,9 @@ const opRemove = (op: Automerge.Diff, [map, ops]: any) => {
const key = path[path.length - 1] const key = path[path.length - 1]
if (key === 'cursors' || op.key === 'cursors') return [map, ops]
const fn = key === 'text' ? removeTextOp : removeNodeOp const fn = key === 'text' ? removeTextOp : removeNodeOp
return [map, [...ops, fn(op)]] return [map, [...ops, fn(op)(map, tmpDoc)]]
} catch (e) { } catch (e) {
console.error(e, op, toJS(map)) console.error(e, op, toJS(map))

View File

@ -1,29 +1,42 @@
import * as Automerge from 'automerge' import * as Automerge from 'automerge'
import { Element, Node } from 'slate'
import { toSlatePath, toJS } from '../utils' import { toSlatePath, toJS } from '../utils'
const setDataOp = ( export const setDataOp = (
{ key = '', obj, path, value }: Automerge.Diff, { key = '', obj, path, value }: Automerge.Diff,
doc: any doc: any
) => (map: any) => { ) => (map: any, tmpDoc: Element) => {
const slatePath = toSlatePath(path)
const node = Node.get(tmpDoc, slatePath)
const oldValue = node[key]
const newValue = toJS(map?.[value] || value)
map[obj] = node // node from tmpDoc is the newest value at the moment, keep map sync
if (newValue == null) {
// slate does this check.
delete node[key]
} else {
node[key] = newValue
}
return { return {
type: 'set_node', type: 'set_node',
path: toSlatePath(path), path: slatePath,
properties: { properties: {
[key]: toJS(Automerge.getObjectById(doc, obj)?.[key]) [key]: toJS(oldValue)
}, },
newProperties: { newProperties: {
[key]: map?.[value] || value [key]: toJS(newValue)
} }
} }
} }
const opSet = (op: Automerge.Diff, [map, ops]: any, doc: any) => { const opSet = (op: Automerge.Diff, [map, ops]: any, doc: any, tmpDoc: any) => {
const { link, value, path, obj, key } = op const { link, value, path, obj, key } = op
try { try {
if (path && path.length && path[0] !== 'cursors') { if (path && path.length && path[0] === 'children') {
ops.push(setDataOp(op, doc)) ops.push(setDataOp(op, doc)(map, tmpDoc))
} else if (map[obj]) { } else if (map[obj]) {
map[obj][key as any] = link ? map[value] : value map[obj][key as any] = link ? map[value] : value
} }

View File

@ -6,6 +6,9 @@ import { CollabAction } from '../model'
export * from './testUtils' export * from './testUtils'
const toJS = (node: any) => { const toJS = (node: any) => {
if (node === undefined) {
return undefined
}
try { try {
return JSON.parse(JSON.stringify(node)) return JSON.parse(JSON.stringify(node))
} catch (e) { } catch (e) {