Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support marks on element nodes #63

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 36 additions & 4 deletions src/plugins/sync-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,9 @@ const createNodeFromYElement = (el, schema, mapping, snapshot, prevSnapshot, com
attrs.ychange = computeYChange ? computeYChange('added', /** @type {Y.Item} */ (el._item).id) : { type: 'added' }
}
}
const node = schema.node(el.nodeName, attrs, children)
const marks = attrs.marks && attrs.marks.map(mark => schema.markFromJSON(mark))
delete attrs.marks
const node = schema.node(el.nodeName, attrs, children, marks)
mapping.set(el, node)
return node
} catch (e) {
Expand Down Expand Up @@ -520,6 +522,9 @@ const createTypeFromElementNode = (node, mapping) => {
type.setAttribute(key, val)
}
}
if (node.marks.length) {
type.setAttribute('marks', node.marks.map(mark => mark.toJSON()))
}
type.insert(0, normalizePNodeContent(node).map(n => createTypeFromTextOrElementNode(n, mapping)))
mapping.set(type, node)
return type
Expand All @@ -545,6 +550,18 @@ const equalAttrs = (pattrs, yattrs) => {
return eq
}

/**
* Compares Prosemirror marks using their serialized `toJSON()` forms.
* https://github.com/ProseMirror/prosemirror-model/blob/master/src/mark.js
*/
const equalMarks = (pmarks, ymarks) => {
if (pmarks.length !== ymarks.length) return false
for (let i = 0; i < pmarks.length; i++) {
if (pmarks[i].type !== ymarks[i].type || !equalAttrs(pmarks[i].attrs, ymarks[i].attrs)) return false
}
return true
}

/**
* @typedef {Array<Array<PModel.Node>|PModel.Node>} NormalizedPNodeContent
*/
Expand Down Expand Up @@ -582,13 +599,25 @@ const equalYTextPText = (ytext, ptexts) => {
}

/**
* Compares ytype with pnode (Prosemirror node) by looking at normalized content, attrs, marks, and children
*
* @param {Y.XmlElement|Y.XmlText|Y.XmlHook} ytype
* @param {any|Array<any>} pnode
*/
const equalYTypePNode = (ytype, pnode) => {
if (ytype instanceof Y.XmlElement && !(pnode instanceof Array) && matchNodeName(ytype, pnode)) {
const normalizedContent = normalizePNodeContent(pnode)
return ytype._length === normalizedContent.length && equalAttrs(ytype.getAttributes(), pnode.attrs) && ytype.toArray().every((ychild, i) => equalYTypePNode(ychild, normalizedContent[i]))
let normalizedContent = normalizePNodeContent(pnode)
if (normalizedContent.length !== ytype._length) return false
// Exclude `marks` attribute from comparison with `pnode.attrs` if it exists as attribute on ytype.
let yattrs = ytype.getAttributes()
let pattrs = pnode.attrs
delete yattrs.marks
if (!equalAttrs(yattrs, pattrs)) return false
// Serialize `pnode.marks` so it is in the same form as `marks` in ytype.
let ymarks = ytype.getAttribute('marks') || []
let pmarks = pnode.marks.map(mark => mark.toJSON())
if (!equalMarks(ymarks, pmarks)) return false
return ytype.toArray().every((ychild, i) => equalYTypePNode(ychild, normalizedContent[i]))
}
return ytype instanceof Y.XmlText && pnode instanceof Array && equalYTextPText(ytype, pnode)
}
Expand Down Expand Up @@ -700,7 +729,7 @@ export const updateYFragment = (y, yDomFragment, pNode, mapping) => {
throw new Error('node name mismatch!')
}
mapping.set(yDomFragment, pNode)
// update attributes
// update attributes and marks
if (yDomFragment instanceof Y.XmlElement) {
const yDomAttrs = yDomFragment.getAttributes()
const pAttrs = pNode.attrs
Expand All @@ -719,6 +748,9 @@ export const updateYFragment = (y, yDomFragment, pNode, mapping) => {
yDomFragment.removeAttribute(key)
}
}
if (pNode.marks.length) {
yDomFragment.setAttribute('marks', pNode.marks.map(mark => mark.toJSON()))
}
}
// update children
const pChildren = normalizePNodeContent(pNode)
Expand Down