mirror of
https://github.com/yjs/yjs.git
synced 2025-12-28 16:06:36 +01:00
Implement & test getContentDeep for all types. Improve ability to compare things using lib0/traits.
This commit is contained in:
8
package-lock.json
generated
8
package-lock.json
generated
@@ -9,7 +9,7 @@
|
|||||||
"version": "13.6.27",
|
"version": "13.6.27",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lib0": "^0.2.104",
|
"lib0": "^0.2.105",
|
||||||
"y-protocols": "^1.0.5"
|
"y-protocols": "^1.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -2774,9 +2774,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lib0": {
|
"node_modules/lib0": {
|
||||||
"version": "0.2.104",
|
"version": "0.2.105",
|
||||||
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.104.tgz",
|
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.105.tgz",
|
||||||
"integrity": "sha512-1tqKRANSPTcjs/yjPoKh52oRM2u5AYdd8jie8sDiN8/5kpWWiQSHUGgtB4VEXLw1chVL3QPSPp8q9RWqzSn2FA==",
|
"integrity": "sha512-5vtbuBi2P43ZYOfVMV+TZYkWEa0J9kijXirzEgrPA+nJDQCtMx805/rqW4G1nXbM9IRIhwW+OyNNgcQdbhKfSw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"isomorphic.js": "^0.2.4"
|
"isomorphic.js": "^0.2.4"
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://docs.yjs.dev",
|
"homepage": "https://docs.yjs.dev",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lib0": "^0.2.104",
|
"lib0": "^0.2.105",
|
||||||
"y-protocols": "^1.0.5"
|
"y-protocols": "^1.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ import {
|
|||||||
ContentAny,
|
ContentAny,
|
||||||
ContentBinary,
|
ContentBinary,
|
||||||
getItemCleanStart,
|
getItemCleanStart,
|
||||||
ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item,
|
ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, createAttributionFromAttrs, AbstractAttributionManager, // eslint-disable-line
|
||||||
createAttributionFromAttrs, // eslint-disable-line
|
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as delta from '../utils/Delta.js'
|
import * as delta from '../utils/Delta.js'
|
||||||
@@ -22,6 +21,13 @@ import * as error from 'lib0/error'
|
|||||||
import * as math from 'lib0/math'
|
import * as math from 'lib0/math'
|
||||||
import * as log from 'lib0/logging'
|
import * as log from 'lib0/logging'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {delta.ArrayDelta|delta.TextDelta|{ children: delta.ArrayDelta<Array<YXmlDeepContent>> }|{ children: delta.ArrayDelta, attributes: {[key:string]:{ value: any, prevValue: any, attribution: import('../utils/AttributionManager.js').Attribution } } }} YXmlDeepContent
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @typedef {delta.ArrayDelta|delta.TextDelta|{ children: delta.ArrayDelta<Array<DeepContent>> }|{ children: delta.ArrayDelta, attributes: {[key:string]:{ value: any, prevValue: any, attribution: import('../utils/AttributionManager.js').Attribution} } }} DeepContent
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://docs.yjs.dev/getting-started/working-with-shared-types#caveats
|
* https://docs.yjs.dev/getting-started/working-with-shared-types#caveats
|
||||||
*/
|
*/
|
||||||
@@ -406,6 +412,22 @@ export class AbstractType {
|
|||||||
* @return {any}
|
* @return {any}
|
||||||
*/
|
*/
|
||||||
toJSON () {}
|
toJSON () {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {AbstractAttributionManager} _am
|
||||||
|
* @return {any}
|
||||||
|
*/
|
||||||
|
getContent (_am) {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {AbstractAttributionManager} _am
|
||||||
|
* @return {DeepContent}
|
||||||
|
*/
|
||||||
|
getContentDeep (_am) {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -476,17 +498,15 @@ export const typeListToArray = type => {
|
|||||||
* Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the
|
* Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the
|
||||||
* attribution `{ isDeleted: true, .. }`.
|
* attribution `{ isDeleted: true, .. }`.
|
||||||
*
|
*
|
||||||
* @template MapType
|
|
||||||
* @param {AbstractType<any>} type
|
* @param {AbstractType<any>} type
|
||||||
* @param {import('../internals.js').AbstractAttributionManager} am
|
* @param {import('../internals.js').AbstractAttributionManager} am
|
||||||
* @return {delta.Delta<Array<MapType>>} The Delta representation of this type.
|
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const typeListGetContent = (type, am) => {
|
export const typeListGetContent = (type, am) => {
|
||||||
type.doc ?? warnPrematureAccess()
|
type.doc ?? warnPrematureAccess()
|
||||||
const d = /** @type {delta.DeltaBuilder<Array<MapType>>} */ (delta.create())
|
const d = delta.createArrayDelta()
|
||||||
/**
|
/**
|
||||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||||
*/
|
*/
|
||||||
@@ -502,7 +522,7 @@ export const typeListGetContent = (type, am) => {
|
|||||||
d.insert(content.getContent(), null, attribution)
|
d.insert(content.getContent(), null, attribution)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return d.done()
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1016,7 +1036,6 @@ export const typeMapGetContent = (parent, am) => {
|
|||||||
return mapcontent
|
return mapcontent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {AbstractType<any>} parent
|
* @param {AbstractType<any>} parent
|
||||||
* @param {string} key
|
* @param {string} key
|
||||||
|
|||||||
@@ -17,10 +17,13 @@ import {
|
|||||||
callTypeObservers,
|
callTypeObservers,
|
||||||
transact,
|
transact,
|
||||||
warnPrematureAccess,
|
warnPrematureAccess,
|
||||||
ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item, // eslint-disable-line
|
typeListGetContent,
|
||||||
AbstractAttributionManager
|
typeListSlice,
|
||||||
|
noAttributionsManager,
|
||||||
|
AbstractAttributionManager, ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
import { typeListGetContent, typeListSlice } from './AbstractType.js'
|
|
||||||
|
import * as delta from '../utils/Delta.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event that describes the changes on a YArray
|
* Event that describes the changes on a YArray
|
||||||
@@ -216,11 +219,31 @@ export class YArray extends AbstractType {
|
|||||||
* attribution `{ isDeleted: true, .. }`.
|
* attribution `{ isDeleted: true, .. }`.
|
||||||
*
|
*
|
||||||
* @param {AbstractAttributionManager} am
|
* @param {AbstractAttributionManager} am
|
||||||
* @return {import('../utils/Delta.js').Delta<Array<T>>} The Delta representation of this type.
|
* @return {import('../utils/Delta.js').ArrayDelta<Array<import('../types/AbstractType.js').DeepContent>>} The Delta representation of this type.
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
getContent (am) {
|
getContentDeep (am = noAttributionsManager) {
|
||||||
|
return this.getContent(am).map(d => /** @type {any} */ (
|
||||||
|
d instanceof delta.InsertOp && d.insert instanceof Array
|
||||||
|
? new delta.InsertOp(d.insert.map(e => e instanceof AbstractType ? e.getContentDeep(am) : e), d.attributes, d.attribution)
|
||||||
|
: d
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the difference to another ydoc (which can be empty) and highlight the differences with
|
||||||
|
* attributions.
|
||||||
|
*
|
||||||
|
* Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the
|
||||||
|
* attribution `{ isDeleted: true, .. }`.
|
||||||
|
*
|
||||||
|
* @param {AbstractAttributionManager} am
|
||||||
|
* @return {import('../utils/Delta.js').ArrayDelta<Array<T>>} The Delta representation of this type.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
getContent (am = noAttributionsManager) {
|
||||||
return typeListGetContent(this, am)
|
return typeListGetContent(this, am)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,13 +13,11 @@ import {
|
|||||||
YMapRefID,
|
YMapRefID,
|
||||||
callTypeObservers,
|
callTypeObservers,
|
||||||
transact,
|
transact,
|
||||||
|
typeMapGetContent,
|
||||||
warnPrematureAccess,
|
warnPrematureAccess,
|
||||||
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item, // eslint-disable-line
|
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line
|
||||||
createAttributionFromAttrs,
|
|
||||||
typeMapGetContent
|
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as array from 'lib0/array'
|
|
||||||
import * as iterator from 'lib0/iterator'
|
import * as iterator from 'lib0/iterator'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1000,7 +1000,27 @@ export class YText extends AbstractType {
|
|||||||
* attribution `{ isDeleted: true, .. }`.
|
* attribution `{ isDeleted: true, .. }`.
|
||||||
*
|
*
|
||||||
* @param {AbstractAttributionManager} am
|
* @param {AbstractAttributionManager} am
|
||||||
* @return {import('../utils/Delta.js').Delta<import('../utils/Delta.js').TextDeltaContent>} The Delta representation of this type.
|
* @return {import('../utils/Delta.js').TextDelta<string | import('./AbstractType.js').DeepContent >} The Delta representation of this type.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
getContentDeep (am = noAttributionsManager) {
|
||||||
|
return this.getContent(am).map(d =>
|
||||||
|
d instanceof delta.InsertOp && d.insert instanceof AbstractType
|
||||||
|
? new delta.InsertOp(d.insert.getContent(am), d.attributes, d.attribution)
|
||||||
|
: d
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the difference to another ydoc (which can be empty) and highlight the differences with
|
||||||
|
* attributions.
|
||||||
|
*
|
||||||
|
* Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the
|
||||||
|
* attribution `{ isDeleted: true, .. }`.
|
||||||
|
*
|
||||||
|
* @param {AbstractAttributionManager} am
|
||||||
|
* @return {import('../utils/Delta.js').TextDelta} The Delta representation of this type.
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
@@ -1068,7 +1088,7 @@ export class YText extends AbstractType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return d.done()
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ import {
|
|||||||
YXmlElementRefID,
|
YXmlElementRefID,
|
||||||
typeMapGetContent,
|
typeMapGetContent,
|
||||||
noAttributionsManager,
|
noAttributionsManager,
|
||||||
Snapshot, YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, // eslint-disable-line
|
AbstractAttributionManager, Snapshot, YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
|
import * as delta from '../utils/Delta.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object|number|null|Array<any>|string|Uint8Array|AbstractType<any>} ValueTypes
|
* @typedef {Object|number|null|Array<any>|string|Uint8Array|AbstractType<any>} ValueTypes
|
||||||
*/
|
*/
|
||||||
@@ -208,6 +210,36 @@ export class YXmlElement extends YXmlFragment {
|
|||||||
return /** @type {any} */ (snapshot ? typeMapGetAllSnapshot(this, snapshot) : typeMapGetAll(this))
|
return /** @type {any} */ (snapshot ? typeMapGetAllSnapshot(this, snapshot) : typeMapGetAll(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the difference to another ydoc (which can be empty) and highlight the differences with
|
||||||
|
* attributions.
|
||||||
|
*
|
||||||
|
* Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the
|
||||||
|
* attribution `{ isDeleted: true, .. }`.
|
||||||
|
*
|
||||||
|
* @param {AbstractAttributionManager} am
|
||||||
|
* @return {{ nodeName: string, children: delta.ArrayDelta<Array<import('./AbstractType.js').DeepContent>>, attributes: import('./AbstractType.js').MapAttributedContent<any> }}
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
getContentDeep (am = noAttributionsManager) {
|
||||||
|
const { children: origChildren, attributes: origAttributes } = this.getContent(am)
|
||||||
|
const children = origChildren.map(d => /** @type {any} */ (
|
||||||
|
(d instanceof delta.InsertOp && d.insert instanceof Array)
|
||||||
|
? new delta.InsertOp(d.insert.map(e => e instanceof AbstractType ? /** @type {delta.ArrayDelta<Array<any>>} */ (e.getContentDeep(am)) : e), d.attributes, d.attribution)
|
||||||
|
: d
|
||||||
|
))
|
||||||
|
/**
|
||||||
|
* @todo there is a Attributes type and a DeepAttributes type.
|
||||||
|
* @type {import('./AbstractType.js').MapAttributedContent<any>}
|
||||||
|
*/
|
||||||
|
const attributes = {}
|
||||||
|
object.forEach(origAttributes, (v, key) => {
|
||||||
|
attributes[key] = Object.assign({}, v, { value: v.value instanceof AbstractType ? v.value.getContentDeep(am) : v.value })
|
||||||
|
})
|
||||||
|
return { nodeName: this.nodeName, children, attributes }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render the difference to another ydoc (which can be empty) and highlight the differences with
|
* Render the difference to another ydoc (which can be empty) and highlight the differences with
|
||||||
* attributions.
|
* attributions.
|
||||||
|
|||||||
@@ -383,13 +383,28 @@ export class YXmlFragment extends AbstractType {
|
|||||||
* Calculate the attributed content using the attribution manager.
|
* Calculate the attributed content using the attribution manager.
|
||||||
*
|
*
|
||||||
* @param {import('../internals.js').AbstractAttributionManager} am
|
* @param {import('../internals.js').AbstractAttributionManager} am
|
||||||
* @return {{ children: import('../utils/Delta.js').Delta<Array<YXmlElement|YXmlText|YXmlHook>> }}
|
* @return {{ children: import('../utils/Delta.js').ArrayDelta<Array<YXmlElement|YXmlText|YXmlHook>> }}
|
||||||
*/
|
*/
|
||||||
getContent (am = noAttributionsManager) {
|
getContent (am = noAttributionsManager) {
|
||||||
const children = typeListGetContent(this, am)
|
const children = typeListGetContent(this, am)
|
||||||
return { children }
|
return { children }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the attributed content using the attribution manager.
|
||||||
|
*
|
||||||
|
* @param {import('../internals.js').AbstractAttributionManager} am
|
||||||
|
* @return {{ children: import('../utils/Delta.js').ArrayDelta<Array<import('./AbstractType.js').YXmlDeepContent>> }}
|
||||||
|
*/
|
||||||
|
getContentDeep (am) {
|
||||||
|
const { children: origChildren } = this.getContent()
|
||||||
|
/**
|
||||||
|
* @type {import('../utils/Delta.js').ArrayDelta<Array<import('./AbstractType.js').YXmlDeepContent>>}
|
||||||
|
*/
|
||||||
|
const children = origChildren.map(d => /** @type {any} */ (d instanceof AbstractType ? d.getContentDeep(am) : d))
|
||||||
|
return { children }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends content to this YArray.
|
* Appends content to this YArray.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as object from 'lib0/object'
|
import * as object from 'lib0/object'
|
||||||
import * as fun from 'lib0/function'
|
import * as fun from 'lib0/function'
|
||||||
|
import * as traits from 'lib0/traits'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {string|Array<any>|{[key: string]: any}} Content
|
* @template {string|Array<any>|{[key: string]: any}} Content
|
||||||
@@ -29,9 +30,20 @@ export class InsertOp {
|
|||||||
this.attribution = attribution
|
this.attribution = attribution
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get length () {
|
||||||
|
return (this.insert.constructor === Array || this.insert.constructor === String) ? this.insert.length : 1
|
||||||
|
}
|
||||||
|
|
||||||
toJSON () {
|
toJSON () {
|
||||||
return object.assign({ insert: this.insert }, this.attributes ? { attributes: this.attributes } : ({}), this.attribution ? { attribution: this.attribution } : ({}))
|
return object.assign({ insert: this.insert }, this.attributes ? { attributes: this.attributes } : ({}), this.attribution ? { attribution: this.attribution } : ({}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {InsertOp<Content>} other
|
||||||
|
*/
|
||||||
|
[traits.EqualityTraitSymbol] (other) {
|
||||||
|
return fun.equalityDeep(this.insert, other.insert) && fun.equalityDeep(this.attributes, other.attributes) && fun.equalityDeep(this.attribution, other.attribution)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DeleteOp {
|
export class DeleteOp {
|
||||||
@@ -42,9 +54,20 @@ export class DeleteOp {
|
|||||||
this.delete = len
|
this.delete = len
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get length () {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
toJSON () {
|
toJSON () {
|
||||||
return { delete: this.delete }
|
return { delete: this.delete }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {DeleteOp} other
|
||||||
|
*/
|
||||||
|
[traits.EqualityTraitSymbol] (other) {
|
||||||
|
return this.delete === other.delete
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RetainOp {
|
export class RetainOp {
|
||||||
@@ -59,16 +82,48 @@ export class RetainOp {
|
|||||||
this.attribution = attribution
|
this.attribution = attribution
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get length () {
|
||||||
|
return this.retain
|
||||||
|
}
|
||||||
|
|
||||||
toJSON () {
|
toJSON () {
|
||||||
return object.assign({ retain: this.retain }, this.attributes ? { attributes: this.attributes } : {}, this.attribution ? { attribution: this.attribution } : {})
|
return object.assign({ retain: this.retain }, this.attributes ? { attributes: this.attributes } : {}, this.attribution ? { attribution: this.attribution } : {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {RetainOp} other
|
||||||
|
*/
|
||||||
|
[traits.EqualityTraitSymbol] (other) {
|
||||||
|
return this.retain === other.retain && fun.equalityDeep(this.attributes, other.attributes) && fun.equalityDeep(this.attribution, other.attribution)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {string|Array<any>|{[key: string]: any}} Content
|
* @typedef {Array<any>} ArrayDeltaContent
|
||||||
*/
|
*/
|
||||||
export class Delta {
|
|
||||||
constructor () {
|
/**
|
||||||
|
* @typedef {string | { [key: string]: any }} TextDeltaContent
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{ array: ArrayDeltaContent, text: TextDeltaContent, custom: string|Array<any>|{[key:string]:any}}} DeltaTypeMapper
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {(TextDelta | ArrayDelta)} Delta
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {'array' | 'text' | 'custom'} Type
|
||||||
|
* @template {DeltaTypeMapper[Type]} [Content=DeltaTypeMapper[Type]]
|
||||||
|
*/
|
||||||
|
export class AbstractDelta {
|
||||||
|
/**
|
||||||
|
* @param {Type} type
|
||||||
|
*/
|
||||||
|
constructor (type) {
|
||||||
|
this.type = type
|
||||||
/**
|
/**
|
||||||
* @type {Array<DeltaOp<Content>>}
|
* @type {Array<DeltaOp<Content>>}
|
||||||
*/
|
*/
|
||||||
@@ -76,48 +131,49 @@ export class Delta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Delta<Content>} d
|
* @template {DeltaTypeMapper[Type]} MContent
|
||||||
|
* @param {(d:DeltaOp<Content>)=>DeltaOp<MContent>} f
|
||||||
|
* @return {DeltaBuilder<Type, MContent>}
|
||||||
|
*/
|
||||||
|
map (f) {
|
||||||
|
const d = /** @type {DeltaBuilder<Type,any>} */ (new /** @type {any} */ (this.constructor)(this.type))
|
||||||
|
d.ops = this.ops.map(f)
|
||||||
|
// @ts-ignore
|
||||||
|
d._lastOp = d.ops[d.ops.length - 1] ?? null
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {(d:DeltaOp<Content>,index:number)=>void} f
|
||||||
|
*/
|
||||||
|
forEach (f) {
|
||||||
|
for (
|
||||||
|
let i = 0, index = 0, op = this.ops[i];
|
||||||
|
i < this.ops.length;
|
||||||
|
i++, index += op.length, op = this.ops[i]
|
||||||
|
) {
|
||||||
|
f(op, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {AbstractDelta<Type, Content>} other
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
equals (d) {
|
equals (other) {
|
||||||
return this.ops.length === d.ops.length && this.ops.every((op, i) => {
|
return this[traits.EqualityTraitSymbol](other)
|
||||||
const dop = d.ops[i]
|
|
||||||
if (op.constructor !== dop.constructor) return false
|
|
||||||
switch (op.constructor) {
|
|
||||||
case DeleteOp: {
|
|
||||||
if (/** @type {DeleteOp} */ (op).delete !== /** @type {DeleteOp} */ (dop).delete) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case InsertOp: {
|
|
||||||
if (
|
|
||||||
!fun.equalityDeep(/** @type {InsertOp<Content>} */ (op).insert, /** @type {InsertOp<Content>} */ (dop).insert)
|
|
||||||
|| !fun.equalityDeep(/** @type {InsertOp<Content>} */ (op).attributes, /** @type {InsertOp<Content>} */ (dop).attributes)
|
|
||||||
|| !fun.equalityDeep(/** @type {InsertOp<Content>} */ (op).attribution, /** @type {InsertOp<Content>} */ (dop).attribution)
|
|
||||||
) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case RetainOp: {
|
|
||||||
if (
|
|
||||||
/** @type {RetainOp} */ (op).retain !== /** @type {RetainOp} */ (dop).retain
|
|
||||||
|| !fun.equalityDeep(/** @type {RetainOp} */ (op).attributes, /** @type {RetainOp} */ (dop).attributes)
|
|
||||||
|| !fun.equalityDeep(/** @type {RetainOp} */ (op).attribution, /** @type {RetainOp} */ (dop).attribution)
|
|
||||||
) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON () {
|
toJSON () {
|
||||||
return { ops: this.ops.map(o => o.toJSON()) }
|
return { ops: this.ops.map(o => o.toJSON()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {AbstractDelta<Type,Content>} other
|
||||||
|
*/
|
||||||
|
[traits.EqualityTraitSymbol] (other) {
|
||||||
|
return this.type === other.type && fun.equalityDeep(this.ops, other.ops)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,12 +190,16 @@ const mergeAttrs = (a, b) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {string|Array<any>|{[key: string]: any}} Content
|
* @template {'array' | 'text' | 'custom'} [Type='custom']
|
||||||
* @extends Delta<Content>
|
* @template {DeltaTypeMapper[Type]} [Content=DeltaTypeMapper[Type]]
|
||||||
|
* @extends AbstractDelta<Type,Content>
|
||||||
*/
|
*/
|
||||||
export class DeltaBuilder extends Delta {
|
export class DeltaBuilder extends AbstractDelta {
|
||||||
constructor () {
|
/**
|
||||||
super()
|
* @param {Type} type
|
||||||
|
*/
|
||||||
|
constructor (type) {
|
||||||
|
super(type)
|
||||||
/**
|
/**
|
||||||
* @type {FormattingAttributes?}
|
* @type {FormattingAttributes?}
|
||||||
*/
|
*/
|
||||||
@@ -230,31 +290,39 @@ export class DeltaBuilder extends Delta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return {Delta<Content>}
|
* @return {AbstractDelta<Type,Content>}
|
||||||
*/
|
*/
|
||||||
done () {
|
done () {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const create = () => new DeltaBuilder()
|
/**
|
||||||
|
* @template {ArrayDeltaContent} [Content=ArrayDeltaContent]
|
||||||
|
* @extends DeltaBuilder<'array',Content>
|
||||||
|
*/
|
||||||
|
export class ArrayDelta extends DeltaBuilder {
|
||||||
|
constructor () {
|
||||||
|
super('array')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {string | { [key: string]: any }} TextDeltaContent
|
* @template {TextDeltaContent} [Content=TextDeltaContent]
|
||||||
|
* @extends DeltaBuilder<'text',Content>
|
||||||
*/
|
*/
|
||||||
|
export class TextDelta extends DeltaBuilder {
|
||||||
|
constructor () {
|
||||||
|
super('text')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {TextDeltaContent} Content
|
* @return {TextDelta<TextDeltaContent>}
|
||||||
* @return {DeltaBuilder<Content>}
|
|
||||||
*/
|
*/
|
||||||
export const createTextDelta = () => new DeltaBuilder()
|
export const createTextDelta = () => new TextDelta()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Array<any>} ArrayDeltaContent
|
* @return {ArrayDelta<ArrayDeltaContent>}
|
||||||
*/
|
*/
|
||||||
|
export const createArrayDelta = () => new ArrayDelta()
|
||||||
/**
|
|
||||||
* @template {ArrayDeltaContent} Content
|
|
||||||
* @return {DeltaBuilder<Content>}
|
|
||||||
*/
|
|
||||||
export const createArrayDelta = () => new DeltaBuilder()
|
|
||||||
|
|||||||
@@ -416,7 +416,6 @@ export const createInsertionSetFromStructStore = ss => {
|
|||||||
return idset
|
return idset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {IdSetEncoderV1 | IdSetEncoderV2} encoder
|
* @param {IdSetEncoderV1 | IdSetEncoderV2} encoder
|
||||||
* @param {IdSet} idSet
|
* @param {IdSet} idSet
|
||||||
|
|||||||
@@ -135,6 +135,9 @@
|
|||||||
"lib0/symbol.js": "./node_modules/lib0/symbol.js",
|
"lib0/symbol.js": "./node_modules/lib0/symbol.js",
|
||||||
"lib0/dist/symbol.cjs": "./node_modules/lib0/dist/symbol.cjs",
|
"lib0/dist/symbol.cjs": "./node_modules/lib0/dist/symbol.cjs",
|
||||||
"lib0/symbol": "./node_modules/lib0/symbol.js",
|
"lib0/symbol": "./node_modules/lib0/symbol.js",
|
||||||
|
"lib0/traits.js": "./node_modules/lib0/traits.js",
|
||||||
|
"lib0/dist/traits.cjs": "./node_modules/lib0/dist/traits.cjs",
|
||||||
|
"lib0/traits": "./node_modules/lib0/traits.js",
|
||||||
"lib0/testing.js": "./node_modules/lib0/testing.js",
|
"lib0/testing.js": "./node_modules/lib0/testing.js",
|
||||||
"lib0/dist/testing.cjs": "./node_modules/lib0/dist/testing.cjs",
|
"lib0/dist/testing.cjs": "./node_modules/lib0/dist/testing.cjs",
|
||||||
"lib0/testing": "./node_modules/lib0/testing.js",
|
"lib0/testing": "./node_modules/lib0/testing.js",
|
||||||
@@ -296,6 +299,9 @@
|
|||||||
"lib0/symbol.js": "./node_modules/lib0/symbol.js",
|
"lib0/symbol.js": "./node_modules/lib0/symbol.js",
|
||||||
"lib0/dist/symbol.cjs": "./node_modules/lib0/dist/symbol.cjs",
|
"lib0/dist/symbol.cjs": "./node_modules/lib0/dist/symbol.cjs",
|
||||||
"lib0/symbol": "./node_modules/lib0/symbol.js",
|
"lib0/symbol": "./node_modules/lib0/symbol.js",
|
||||||
|
"lib0/traits.js": "./node_modules/lib0/traits.js",
|
||||||
|
"lib0/dist/traits.cjs": "./node_modules/lib0/dist/traits.cjs",
|
||||||
|
"lib0/traits": "./node_modules/lib0/traits.js",
|
||||||
"lib0/testing.js": "./node_modules/lib0/testing.js",
|
"lib0/testing.js": "./node_modules/lib0/testing.js",
|
||||||
"lib0/dist/testing.cjs": "./node_modules/lib0/dist/testing.cjs",
|
"lib0/dist/testing.cjs": "./node_modules/lib0/dist/testing.cjs",
|
||||||
"lib0/testing": "./node_modules/lib0/testing.js",
|
"lib0/testing": "./node_modules/lib0/testing.js",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as t from 'lib0/testing'
|
import * as t from 'lib0/testing'
|
||||||
import * as idmap from '../src/utils/IdMap.js'
|
import * as idmap from '../src/utils/IdMap.js'
|
||||||
import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap, createAttribution, validateIdMap } from './testHelper.js'
|
import { compareIdmaps, createIdMap, ID, createRandomIdSet, createRandomIdMap, createAttribution } from './testHelper.js'
|
||||||
import * as YY from '../src/internals.js'
|
import * as YY from '../src/internals.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import * as delta from '../src/utils/Delta.js'
|
|||||||
* @param {t.TestCase} _tc
|
* @param {t.TestCase} _tc
|
||||||
*/
|
*/
|
||||||
export const testDelta = _tc => {
|
export const testDelta = _tc => {
|
||||||
const d = delta.create().insert('hello').insert(' ').useAttributes({ bold: true }).insert('world').useAttribution({ insert: ['tester'] }).insert('!').done()
|
const d = delta.createTextDelta().insert('hello').insert(' ').useAttributes({ bold: true }).insert('world').useAttribution({ insert: ['tester'] }).insert('!').done()
|
||||||
t.compare(d.toJSON().ops, [{ insert: 'hello ' }, { insert: 'world', attributes: { bold: true } }, { insert: '!', attributes: { bold: true }, attribution: { insert: ['tester'] } }])
|
t.compare(d.toJSON().ops, [{ insert: 'hello ' }, { insert: 'world', attributes: { bold: true } }, { insert: '!', attributes: { bold: true }, attribution: { insert: ['tester'] } }])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ export const testDelta = _tc => {
|
|||||||
* @param {t.TestCase} _tc
|
* @param {t.TestCase} _tc
|
||||||
*/
|
*/
|
||||||
export const testDeltaMerging = _tc => {
|
export const testDeltaMerging = _tc => {
|
||||||
const d = delta.create()
|
const d = delta.createTextDelta()
|
||||||
.insert('hello')
|
.insert('hello')
|
||||||
.insert('world')
|
.insert('world')
|
||||||
.insert(' ', { italic: true })
|
.insert(' ', { italic: true })
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ export const testAfterTransactionRecursion = _tc => {
|
|||||||
}, 'test')
|
}, 'test')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {t.TestCase} _tc
|
* @param {t.TestCase} _tc
|
||||||
*/
|
*/
|
||||||
@@ -68,7 +67,6 @@ export const testFindTypeInOtherDoc = _tc => {
|
|||||||
t.assert(findTypeInOtherYdoc(ytext, ydocClone) != null)
|
t.assert(findTypeInOtherYdoc(ytext, ydocClone) != null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {t.TestCase} _tc
|
* @param {t.TestCase} _tc
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -528,8 +528,8 @@ export const testAttributedContent = _tc => {
|
|||||||
yarray.delete(0, 1)
|
yarray.delete(0, 1)
|
||||||
yarray.insert(1, [42])
|
yarray.insert(1, [42])
|
||||||
})
|
})
|
||||||
let expectedContent = delta.createArrayDelta().insert([1], null, { delete: [] }).insert([2]).insert([42], null, { insert: [] })
|
const expectedContent = delta.createArrayDelta().insert([1], null, { delete: [] }).insert([2]).insert([42], null, { insert: [] })
|
||||||
let attributedContent = yarray.getContent(attributionManager)
|
const attributedContent = yarray.getContent(attributionManager)
|
||||||
console.log(attributedContent.toJSON().ops)
|
console.log(attributedContent.toJSON().ops)
|
||||||
t.assert(attributedContent.equals(expectedContent))
|
t.assert(attributedContent.equals(expectedContent))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import * as Y from '../src/index.js'
|
import * as Y from '../src/index.js'
|
||||||
import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint-disable-line
|
import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint-disable-line
|
||||||
import * as delta from '../src/utils/Delta.js'
|
|
||||||
import {
|
import {
|
||||||
compareIDs,
|
compareIDs,
|
||||||
noAttributionsManager,
|
noAttributionsManager,
|
||||||
@@ -631,22 +630,22 @@ export const testAttributedContent = _tc => {
|
|||||||
})
|
})
|
||||||
t.group('initial value', () => {
|
t.group('initial value', () => {
|
||||||
ymap.set('test', 42)
|
ymap.set('test', 42)
|
||||||
let expectedContent = { test: { prevValue: undefined, value: 42, attribution: { insert: [] } } }
|
const expectedContent = { test: { prevValue: undefined, value: 42, attribution: { insert: [] } } }
|
||||||
let attributedContent = ymap.getContent(attributionManager)
|
const attributedContent = ymap.getContent(attributionManager)
|
||||||
console.log(attributedContent)
|
console.log(attributedContent)
|
||||||
t.compare(expectedContent, attributedContent)
|
t.compare(expectedContent, attributedContent)
|
||||||
})
|
})
|
||||||
t.group('overwrite value', () => {
|
t.group('overwrite value', () => {
|
||||||
ymap.set('test', 'fourtytwo')
|
ymap.set('test', 'fourtytwo')
|
||||||
let expectedContent = { test: { prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } } }
|
const expectedContent = { test: { prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } } }
|
||||||
let attributedContent = ymap.getContent(attributionManager)
|
const attributedContent = ymap.getContent(attributionManager)
|
||||||
console.log(attributedContent)
|
console.log(attributedContent)
|
||||||
t.compare(expectedContent, attributedContent)
|
t.compare(expectedContent, attributedContent)
|
||||||
})
|
})
|
||||||
t.group('delete value', () => {
|
t.group('delete value', () => {
|
||||||
ymap.delete('test')
|
ymap.delete('test')
|
||||||
let expectedContent = { test: { prevValue: 'fourtytwo', value: undefined, attribution: { delete: [] } } }
|
const expectedContent = { test: { prevValue: 'fourtytwo', value: undefined, attribution: { delete: [] } } }
|
||||||
let attributedContent = ymap.getContent(attributionManager)
|
const attributedContent = ymap.getContent(attributionManager)
|
||||||
console.log(attributedContent)
|
console.log(attributedContent)
|
||||||
t.compare(expectedContent, attributedContent)
|
t.compare(expectedContent, attributedContent)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2316,15 +2316,15 @@ export const testAttributedContent = _tc => {
|
|||||||
})
|
})
|
||||||
t.group('insert / delete / format', () => {
|
t.group('insert / delete / format', () => {
|
||||||
ytext.applyDelta([{ retain: 4, attributes: { italic: true } }, { retain: 2 }, { delete: 5 }, { insert: 'attributions' }])
|
ytext.applyDelta([{ retain: 4, attributes: { italic: true } }, { retain: 2 }, { delete: 5 }, { insert: 'attributions' }])
|
||||||
let expectedContent = delta.create().insert('Hell', { italic: true }, { attributes: { italic: [] } }).insert('o ').insert('World', {}, { delete: [] }).insert('attributions', {}, { insert: [] }).insert('!')
|
const expectedContent = delta.createTextDelta().insert('Hell', { italic: true }, { attributes: { italic: [] } }).insert('o ').insert('World', {}, { delete: [] }).insert('attributions', {}, { insert: [] }).insert('!')
|
||||||
let attributedContent = ytext.getContent(attributionManager)
|
const attributedContent = ytext.getContent(attributionManager)
|
||||||
console.log(attributedContent.toJSON().ops)
|
console.log(attributedContent.toJSON().ops)
|
||||||
t.assert(attributedContent.equals(expectedContent))
|
t.assert(attributedContent.equals(expectedContent))
|
||||||
})
|
})
|
||||||
t.group('unformat', () => {
|
t.group('unformat', () => {
|
||||||
ytext.applyDelta([{retain: 5, attributes: { italic: null }}])
|
ytext.applyDelta([{ retain: 5, attributes: { italic: null } }])
|
||||||
let expectedContent = delta.create().insert('Hell', null, { attributes: { italic: [] } }).insert('o attributions!')
|
const expectedContent = delta.createTextDelta().insert('Hell', null, { attributes: { italic: [] } }).insert('o attributions!')
|
||||||
let attributedContent = ytext.getContent(attributionManager)
|
const attributedContent = ytext.getContent(attributionManager)
|
||||||
console.log(attributedContent.toJSON().ops)
|
console.log(attributedContent.toJSON().ops)
|
||||||
t.assert(attributedContent.equals(expectedContent))
|
t.assert(attributedContent.equals(expectedContent))
|
||||||
})
|
})
|
||||||
@@ -2355,9 +2355,9 @@ export const testAttributedDiffing = _tc => {
|
|||||||
// implementations is the TwosetAttributionManager
|
// implementations is the TwosetAttributionManager
|
||||||
const attributionManager = new TwosetAttributionManager(attributedInsertions, attributedDeletions)
|
const attributionManager = new TwosetAttributionManager(attributedInsertions, attributedDeletions)
|
||||||
// we render the attributed content with the attributionManager
|
// we render the attributed content with the attributionManager
|
||||||
let attributedContent = ytext.getContent(attributionManager)
|
const attributedContent = ytext.getContent(attributionManager)
|
||||||
console.log(JSON.stringify(attributedContent.toJSON().ops, null, 2))
|
console.log(JSON.stringify(attributedContent.toJSON().ops, null, 2))
|
||||||
let expectedContent = delta.create().insert('Hell', { italic: true }, { attributes: { italic: ['Bob'] } }).insert('o ').insert('World', {}, { delete: ['Bob'] }).insert('attributions', {}, { insert: ['Bob'] }).insert('!')
|
const expectedContent = delta.createTextDelta().insert('Hell', { italic: true }, { attributes: { italic: ['Bob'] } }).insert('o ').insert('World', {}, { delete: ['Bob'] }).insert('attributions', {}, { insert: ['Bob'] }).insert('!')
|
||||||
t.assert(attributedContent.equals(expectedContent))
|
t.assert(attributedContent.equals(expectedContent))
|
||||||
console.log(Y.encodeIdMap(attributedInsertions).length)
|
console.log(Y.encodeIdMap(attributedInsertions).length)
|
||||||
}
|
}
|
||||||
@@ -2588,14 +2588,14 @@ const checkResult = result => {
|
|||||||
*/
|
*/
|
||||||
const typeToObject = d => d.insert instanceof Y.AbstractType ? d.insert.toJSON() : d
|
const typeToObject = d => d.insert instanceof Y.AbstractType ? d.insert.toJSON() : d
|
||||||
|
|
||||||
t.info('length of text = ' + result.users[i-1].getText('text').length)
|
t.info('length of text = ' + result.users[i - 1].getText('text').length)
|
||||||
t.measureTime('original toDelta perf', () => {
|
t.measureTime('original toDelta perf', () => {
|
||||||
result.users[i-1].getText('text').toDelta().map(typeToObject)
|
result.users[i - 1].getText('text').toDelta().map(typeToObject)
|
||||||
})
|
})
|
||||||
t.measureTime('getContent(attributionManager) performance)', () => {
|
t.measureTime('getContent(attributionManager) performance)', () => {
|
||||||
result.users[i-1].getText('text').getContent()
|
result.users[i - 1].getText('text').getContent()
|
||||||
})
|
})
|
||||||
const p1 = result.users[i-1].getText('text').toDelta().map(typeToObject)
|
const p1 = result.users[i - 1].getText('text').toDelta().map(typeToObject)
|
||||||
const p2 = result.users[i].getText('text').toDelta().map(typeToObject)
|
const p2 = result.users[i].getText('text').toDelta().map(typeToObject)
|
||||||
t.compare(p1, p2)
|
t.compare(p1, p2)
|
||||||
}
|
}
|
||||||
@@ -2629,7 +2629,7 @@ export const testAttributionManagerDefaultPerformance = tc => {
|
|||||||
ytext.insert(index, content)
|
ytext.insert(index, content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t.info(`number of changes: ${N/1000}k`)
|
t.info(`number of changes: ${N / 1000}k`)
|
||||||
t.info(`length of text: ${ytext.length}`)
|
t.info(`length of text: ${ytext.length}`)
|
||||||
const M = 100
|
const M = 100
|
||||||
t.measureTime(`original toString perf <executed ${M} times>`, () => {
|
t.measureTime(`original toString perf <executed ${M} times>`, () => {
|
||||||
|
|||||||
@@ -243,8 +243,8 @@ export const testFragmentAttributedContent = _tc => {
|
|||||||
yfragment.delete(0, 1)
|
yfragment.delete(0, 1)
|
||||||
yfragment.insert(1, [elem3])
|
yfragment.insert(1, [elem3])
|
||||||
})
|
})
|
||||||
let expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] })
|
const expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] })
|
||||||
let attributedContent = yfragment.getContent(attributionManager)
|
const attributedContent = yfragment.getContent(attributionManager)
|
||||||
console.log(attributedContent.children.toJSON().ops)
|
console.log(attributedContent.children.toJSON().ops)
|
||||||
t.assert(attributedContent.children.equals(expectedContent))
|
t.assert(attributedContent.children.equals(expectedContent))
|
||||||
t.compare(elem1.getContent(attributionManager).toJSON(), delta.createTextDelta().insert('hello', null, { delete: [] }).done().toJSON())
|
t.compare(elem1.getContent(attributionManager).toJSON(), delta.createTextDelta().insert('hello', null, { delete: [] }).done().toJSON())
|
||||||
@@ -257,8 +257,8 @@ export const testFragmentAttributedContent = _tc => {
|
|||||||
export const testElementAttributedContent = _tc => {
|
export const testElementAttributedContent = _tc => {
|
||||||
const ydoc = new Y.Doc({ gc: false })
|
const ydoc = new Y.Doc({ gc: false })
|
||||||
const yelement = ydoc.getXmlElement('p')
|
const yelement = ydoc.getXmlElement('p')
|
||||||
const elem1 = new Y.XmlElement('span')
|
const elem1 = new Y.XmlText('hello')
|
||||||
const elem2 = new Y.XmlText('hello')
|
const elem2 = new Y.XmlElement('span')
|
||||||
const elem3 = new Y.XmlText('world')
|
const elem3 = new Y.XmlText('world')
|
||||||
yelement.insert(0, [elem1, elem2])
|
yelement.insert(0, [elem1, elem2])
|
||||||
let attributionManager = Y.noAttributionsManager
|
let attributionManager = Y.noAttributionsManager
|
||||||
@@ -272,12 +272,28 @@ export const testElementAttributedContent = _tc => {
|
|||||||
yelement.insert(1, [elem3])
|
yelement.insert(1, [elem3])
|
||||||
yelement.setAttribute('key', '42')
|
yelement.setAttribute('key', '42')
|
||||||
})
|
})
|
||||||
let expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] })
|
const expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] })
|
||||||
let attributedContent = yelement.getContent(attributionManager)
|
const attributedContent = yelement.getContent(attributionManager)
|
||||||
console.log('children', attributedContent.children.toJSON().ops)
|
console.log('children', attributedContent.children.toJSON().ops)
|
||||||
console.log('attributes', attributedContent.attributes)
|
console.log('attributes', attributedContent.attributes)
|
||||||
t.assert(attributedContent.children.equals(expectedContent))
|
t.assert(attributedContent.children.equals(expectedContent))
|
||||||
t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } })
|
t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } })
|
||||||
|
t.group('test getContentDeep', () => {
|
||||||
|
const expectedContent = delta.createArrayDelta().insert(
|
||||||
|
[delta.createTextDelta().insert('hello', null, { delete: [] })],
|
||||||
|
null,
|
||||||
|
{ delete: [] }
|
||||||
|
).insert([{ nodeName: 'span', children: delta.createArrayDelta(), attributes: {} }])
|
||||||
|
.insert([
|
||||||
|
delta.createTextDelta().insert('world', null, { insert: [] })
|
||||||
|
], null, { insert: [] })
|
||||||
|
const attributedContent = yelement.getContentDeep(attributionManager)
|
||||||
|
console.log('children', JSON.stringify(attributedContent.children.toJSON().ops, null, 2))
|
||||||
|
console.log('cs expec', JSON.stringify(expectedContent.toJSON().ops, null, 2))
|
||||||
|
console.log('attributes', attributedContent.attributes)
|
||||||
|
t.assert(attributedContent.children.equals(expectedContent))
|
||||||
|
t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } })
|
||||||
|
t.assert(attributedContent.nodeName === 'UNDEFINED')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user