From 1ed58909d3053f3ce9775ad0f9bd1dfc6721b0a4 Mon Sep 17 00:00:00 2001 From: Kevin Jahns <kevin.jahns@pm.me> Date: Sat, 14 Nov 2020 13:33:43 +0100 Subject: [PATCH] implement prev/nextSibling&firstChild & parent - #259 --- README.md | 18 ++++++++++++++++++ src/types/AbstractType.js | 7 +++++++ src/types/YXmlElement.js | 18 +++++++++++++++++- src/types/YXmlFragment.js | 8 ++++++++ src/types/YXmlText.js | 18 +++++++++++++++++- tests/y-xml.tests.js | 16 ++++++++++++++++ 6 files changed, 83 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 60dcf892..d36b15b8 100644 --- a/README.md +++ b/README.md @@ -241,6 +241,8 @@ necessary. </p> <pre>const yarray = new Y.Array()</pre> <dl> + <b><code>parent:Y.AbstractType|null</code></b> + <dd></dd> <b><code>insert(index:number, content:Array<object|boolean|Array|string|number|Uint8Array|Y.Type>)</code></b> <dd> Insert content at <var>index</var>. Note that content is an array of elements. @@ -312,6 +314,8 @@ or any of its children. </p> <pre><code>const ymap = new Y.Map()</code></pre> <dl> + <b><code>parent:Y.AbstractType|null</code></b> + <dd></dd> <b><code>get(key:string):object|boolean|string|number|Uint8Array|Y.Type</code></b> <dd></dd> <b><code>set(key:string, value:object|boolean|string|number|Uint8Array|Y.Type)</code></b> @@ -391,6 +395,8 @@ YTextEvents compute changes as deltas. </p> <pre>const ytext = new Y.Text()</pre> <dl> + <b><code>parent:Y.AbstractType|null</code></b> + <dd></dd> <b><code>insert(index:number, content:string, [formattingAttributes:Object<string,string>])</code></b> <dd> Insert a string at <var>index</var> and assign formatting attributes to it. @@ -449,6 +455,10 @@ or any of its children. </p> <pre><code>const yxml = new Y.XmlFragment()</code></pre> <dl> + <b><code>parent:Y.AbstractType|null</code></b> + <dd></dd> + <b><code>firstChild:Y.XmlElement|Y.XmlText|null</code></b> + <dd></dd> <b><code>insert(index:number, content:Array<Y.XmlElement|Y.XmlText>)</code></b> <dd></dd> <b><code>delete(index:number, length:number)</code></b> @@ -504,6 +514,14 @@ content and be actually XML compliant. </p> <pre><code>const yxml = new Y.XmlElement()</code></pre> <dl> + <b><code>parent:Y.AbstractType|null</code></b> + <dd></dd> + <b><code>firstChild:Y.XmlElement|Y.XmlText|null</code></b> + <dd></dd> + <b><code>nextSibling:Y.XmlElement|Y.XmlText|null</code></b> + <dd></dd> + <b><code>prevSibling:Y.XmlElement|Y.XmlText|null</code></b> + <dd></dd> <b><code>insert(index:number, content:Array<Y.XmlElement|Y.XmlText>)</code></b> <dd></dd> <b><code>delete(index:number, length:number)</code></b> diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index c3513d68..3b6aa581 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -287,6 +287,13 @@ export class AbstractType { this._searchMarker = null } + /** + * @return {AbstractType<any>|null} + */ + get parent () { + return this._item ? /** @type {AbstractType<any>} */ (this._item.parent) : null + } + /** * Integrate this type into the Yjs instance. * diff --git a/src/types/YXmlElement.js b/src/types/YXmlElement.js index 0757e1b2..3b64fa51 100644 --- a/src/types/YXmlElement.js +++ b/src/types/YXmlElement.js @@ -8,7 +8,7 @@ import { typeMapGetAll, typeListForEach, YXmlElementRefID, - AbstractType, AbstractUpdateDecoder, AbstractUpdateEncoder, Snapshot, Doc, Item // eslint-disable-line + YXmlText, ContentType, AbstractType, AbstractUpdateDecoder, AbstractUpdateEncoder, Snapshot, Doc, Item // eslint-disable-line } from '../internals.js' /** @@ -28,6 +28,22 @@ export class YXmlElement extends YXmlFragment { this._prelimAttrs = new Map() } + /** + * @type {YXmlElement|YXmlText|null} + */ + get nextSibling () { + const n = this._item ? this._item.next : null + return n ? /** @type {YXmlElement|YXmlText} */ (/** @type {ContentType} */ (n.content).type) : null + } + + /** + * @type {YXmlElement|YXmlText|null} + */ + get prevSibling () { + const n = this._item ? this._item.prev : null + return n ? /** @type {YXmlElement|YXmlText} */ (/** @type {ContentType} */ (n.content).type) : null + } + /** * Integrate this type into the Yjs instance. * diff --git a/src/types/YXmlFragment.js b/src/types/YXmlFragment.js index 711c9687..41c581e1 100644 --- a/src/types/YXmlFragment.js +++ b/src/types/YXmlFragment.js @@ -130,6 +130,14 @@ export class YXmlFragment extends AbstractType { this._prelimContent = [] } + /** + * @type {YXmlElement|YXmlText|null} + */ + get firstChild () { + const first = this._first + return first ? first.content.getContent()[0] : null + } + /** * Integrate this type into the Yjs instance. * diff --git a/src/types/YXmlText.js b/src/types/YXmlText.js index 697e31c9..dd8d892d 100644 --- a/src/types/YXmlText.js +++ b/src/types/YXmlText.js @@ -2,7 +2,7 @@ import { YText, YXmlTextRefID, - AbstractUpdateDecoder, AbstractUpdateEncoder // eslint-disable-line + ContentType, YXmlElement, AbstractUpdateDecoder, AbstractUpdateEncoder // eslint-disable-line } from '../internals.js' /** @@ -10,6 +10,22 @@ import { * simple formatting information like bold and italic. */ export class YXmlText extends YText { + /** + * @type {YXmlElement|YXmlText|null} + */ + get nextSibling () { + const n = this._item ? this._item.next : null + return n ? /** @type {YXmlElement|YXmlText} */ (/** @type {ContentType} */ (n.content).type) : null + } + + /** + * @type {YXmlElement|YXmlText|null} + */ + get prevSibling () { + const n = this._item ? this._item.prev : null + return n ? /** @type {YXmlElement|YXmlText} */ (/** @type {ContentType} */ (n.content).type) : null + } + _copy () { return new YXmlText() } diff --git a/tests/y-xml.tests.js b/tests/y-xml.tests.js index d59ee6c8..48132175 100644 --- a/tests/y-xml.tests.js +++ b/tests/y-xml.tests.js @@ -87,3 +87,19 @@ export const testYtextAttributes = tc => { t.compare(ytext.getAttribute('test'), 42) t.compare(ytext.getAttributes(), { test: 42 }) } + +/** + * @param {t.TestCase} tc + */ +export const testSiblings = tc => { + const ydoc = new Y.Doc() + const yxml = ydoc.getXmlFragment() + const first = new Y.XmlText() + const second = new Y.XmlElement('p') + yxml.insert(0, [first, second]) + t.assert(first.nextSibling === second) + t.assert(second.prevSibling === first) + t.assert(first.parent === yxml) + t.assert(yxml.parent === null) + t.assert(yxml.firstChild === first) +}