mirror of
https://github.com/yjs/yjs.git
synced 2025-12-16 19:57:45 +01:00
work on new deltas with modifiers
This commit is contained in:
@@ -417,7 +417,7 @@ export class AbstractType {
|
|||||||
* @param {AbstractAttributionManager} _am
|
* @param {AbstractAttributionManager} _am
|
||||||
* @return {any}
|
* @return {any}
|
||||||
*/
|
*/
|
||||||
getContent (_am) {
|
getDelta (_am) {
|
||||||
error.methodUnimplemented()
|
error.methodUnimplemented()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -980,11 +980,6 @@ export const typeMapGetAll = (parent) => {
|
|||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @template MapType
|
|
||||||
* @typedef {{ [key: string]: { prevValue: MapType | undefined, value: MapType | undefined, attribution: any } }} MapAttributedContent
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
@@ -995,16 +990,12 @@ export const typeMapGetAll = (parent) => {
|
|||||||
* @template MapType
|
* @template MapType
|
||||||
* @param {AbstractType<any>} parent
|
* @param {AbstractType<any>} parent
|
||||||
* @param {import('../internals.js').AbstractAttributionManager} am
|
* @param {import('../internals.js').AbstractAttributionManager} am
|
||||||
* @return {MapAttributedContent<MapType>} The Delta representation of this type.
|
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const typeMapGetContent = (parent, am) => {
|
export const typeMapGetDelta = (parent, am) => {
|
||||||
/**
|
const mapdelta = /** @type {delta.MapDeltaBuilder<{ [key:string]: MapType }>} */ (delta.createMapDelta())
|
||||||
* @type {MapAttributedContent<MapType>}
|
|
||||||
*/
|
|
||||||
const mapcontent = {}
|
|
||||||
parent.doc ?? warnPrematureAccess()
|
parent.doc ?? warnPrematureAccess()
|
||||||
parent._map.forEach((item, key) => {
|
parent._map.forEach((item, key) => {
|
||||||
/**
|
/**
|
||||||
@@ -1016,7 +1007,7 @@ export const typeMapGetContent = (parent, am) => {
|
|||||||
const c = array.last(content.getContent())
|
const c = array.last(content.getContent())
|
||||||
const attribution = createAttributionFromAttributionItems(attrs, deleted)
|
const attribution = createAttributionFromAttributionItems(attrs, deleted)
|
||||||
if (deleted) {
|
if (deleted) {
|
||||||
mapcontent[key] = { prevValue: c, value: undefined, attribution }
|
mapdelta.delete(key, c, attribution)
|
||||||
} else {
|
} else {
|
||||||
/**
|
/**
|
||||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||||
@@ -1038,10 +1029,10 @@ export const typeMapGetContent = (parent, am) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const prevValue = cs.length > 0 ? array.last(cs[0].content.getContent()) : undefined
|
const prevValue = cs.length > 0 ? array.last(cs[0].content.getContent()) : undefined
|
||||||
mapcontent[key] = { prevValue, value: c, attribution }
|
mapdelta.set(key, c, prevValue, attribution)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return mapcontent
|
return mapdelta
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ export class YArray extends AbstractType {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
getContentDeep (am = noAttributionsManager) {
|
getContentDeep (am = noAttributionsManager) {
|
||||||
return this.getContent(am).map(d => /** @type {any} */ (
|
return this.getDelta(am).map(d => /** @type {any} */ (
|
||||||
d instanceof delta.InsertArrayOp && d.insert instanceof Array
|
d instanceof delta.InsertArrayOp && d.insert instanceof Array
|
||||||
? new delta.InsertArrayOp(d.insert.map(e => e instanceof AbstractType ? e.getContentDeep(am) : e), d.attributes, d.attribution)
|
? new delta.InsertArrayOp(d.insert.map(e => e instanceof AbstractType ? e.getContentDeep(am) : e), d.attributes, d.attribution)
|
||||||
: d
|
: d
|
||||||
@@ -243,7 +243,7 @@ export class YArray extends AbstractType {
|
|||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
getContent (am = noAttributionsManager) {
|
getDelta (am = noAttributionsManager) {
|
||||||
return typeListGetContent(this, am)
|
return typeListGetContent(this, am)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ import {
|
|||||||
YMapRefID,
|
YMapRefID,
|
||||||
callTypeObservers,
|
callTypeObservers,
|
||||||
transact,
|
transact,
|
||||||
typeMapGetContent,
|
typeMapGetDelta,
|
||||||
warnPrematureAccess,
|
warnPrematureAccess,
|
||||||
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line
|
MapDelta, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
import * as iterator from 'lib0/iterator'
|
import * as iterator from 'lib0/iterator'
|
||||||
@@ -195,12 +195,12 @@ export class YMap extends AbstractType {
|
|||||||
* attribution `{ isDeleted: true, .. }`.
|
* attribution `{ isDeleted: true, .. }`.
|
||||||
*
|
*
|
||||||
* @param {import('../internals.js').AbstractAttributionManager} am
|
* @param {import('../internals.js').AbstractAttributionManager} am
|
||||||
* @return {import('./AbstractType.js').MapAttributedContent<MapType>} The Delta representation of this type.
|
* @return {MapDelta<{[key:string]: MapType}>} The Delta representation of this type.
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
getContent (am) {
|
getDelta (am) {
|
||||||
return typeMapGetContent(this, am)
|
return typeMapGetDelta(this, am)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -789,7 +789,7 @@ export class YText extends AbstractType {
|
|||||||
* @type {YText<Embeds>}
|
* @type {YText<Embeds>}
|
||||||
*/
|
*/
|
||||||
const text = new YText()
|
const text = new YText()
|
||||||
text.applyDelta(this.getContent())
|
text.applyDelta(this.getDelta())
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -887,23 +887,13 @@ export class YText extends AbstractType {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
getContentDeep (am = noAttributionsManager) {
|
getContentDeep (am = noAttributionsManager) {
|
||||||
return this.getContent(am).map(d =>
|
return this.getDelta(am).map(d =>
|
||||||
d instanceof delta.InsertEmbedOp && d.insert instanceof AbstractType
|
d instanceof delta.InsertEmbedOp && d.insert instanceof AbstractType
|
||||||
? new delta.InsertEmbedOp(d.insert.getContent(am), d.attributes, d.attribution)
|
? new delta.InsertEmbedOp(d.insert.getDelta(am), d.attributes, d.attribution)
|
||||||
: d
|
: d
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {AbstractAttributionManager} am
|
|
||||||
* @return {import('../utils/Delta.js').TextDelta<Embeds>} The Delta representation of this type.
|
|
||||||
*
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
getContent (am = noAttributionsManager) {
|
|
||||||
return this.getDelta(am)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
@@ -1301,7 +1291,7 @@ export class YText extends AbstractType {
|
|||||||
* @param {this} other
|
* @param {this} other
|
||||||
*/
|
*/
|
||||||
[traits.EqualityTraitSymbol] (other) {
|
[traits.EqualityTraitSymbol] (other) {
|
||||||
return this.getContent().equals(other.getContent())
|
return this.getDelta().equals(other.getDelta())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
typeMapGetAllSnapshot,
|
typeMapGetAllSnapshot,
|
||||||
typeListForEach,
|
typeListForEach,
|
||||||
YXmlElementRefID,
|
YXmlElementRefID,
|
||||||
typeMapGetContent,
|
typeMapGetDelta,
|
||||||
noAttributionsManager,
|
noAttributionsManager,
|
||||||
AbstractAttributionManager, 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'
|
||||||
@@ -223,7 +223,7 @@ export class YXmlElement extends YXmlFragment {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
getContentDeep (am = noAttributionsManager) {
|
getContentDeep (am = noAttributionsManager) {
|
||||||
const { children: origChildren, attributes: origAttributes } = this.getContent(am)
|
const { children: origChildren, attributes: origAttributes } = this.getDelta(am)
|
||||||
const children = origChildren.map(d => /** @type {any} */ (
|
const children = origChildren.map(d => /** @type {any} */ (
|
||||||
(d instanceof delta.InsertArrayOp && d.insert instanceof Array)
|
(d instanceof delta.InsertArrayOp && d.insert instanceof Array)
|
||||||
? new delta.InsertArrayOp(d.insert.map(e => e instanceof AbstractType ? /** @type {delta.ArrayDelta<Array<any>>} */ (e.getContentDeep(am)) : e), d.attributes, d.attribution)
|
? new delta.InsertArrayOp(d.insert.map(e => e instanceof AbstractType ? /** @type {delta.ArrayDelta<Array<any>>} */ (e.getContentDeep(am)) : e), d.attributes, d.attribution)
|
||||||
@@ -231,9 +231,9 @@ export class YXmlElement extends YXmlFragment {
|
|||||||
))
|
))
|
||||||
/**
|
/**
|
||||||
* @todo there is a Attributes type and a DeepAttributes type.
|
* @todo there is a Attributes type and a DeepAttributes type.
|
||||||
* @type {import('./AbstractType.js').MapAttributedContent<any>}
|
* @type {delta.MapDelta<>}
|
||||||
*/
|
*/
|
||||||
const attributes = {}
|
const attributes = delta.createMapDelta()
|
||||||
object.forEach(origAttributes, (v, key) => {
|
object.forEach(origAttributes, (v, key) => {
|
||||||
attributes[key] = Object.assign({}, v, { value: v.value instanceof AbstractType ? v.value.getContentDeep(am) : v.value })
|
attributes[key] = Object.assign({}, v, { value: v.value instanceof AbstractType ? v.value.getContentDeep(am) : v.value })
|
||||||
})
|
})
|
||||||
@@ -251,10 +251,10 @@ export class YXmlElement extends YXmlFragment {
|
|||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
getContent (am = noAttributionsManager) {
|
getDelta (am = noAttributionsManager) {
|
||||||
const attributes = typeMapGetContent(this, am)
|
const { children } = super.getDelta(am)
|
||||||
const { children } = super.getContent(am)
|
const attributes = typeMapGetDelta(this, am)
|
||||||
return { nodeName: this.nodeName, children, attributes }
|
return new delta.XmlDelta(this.nodeName, children, attributes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -386,7 +386,7 @@ export class YXmlFragment extends AbstractType {
|
|||||||
* @param {import('../internals.js').AbstractAttributionManager} am
|
* @param {import('../internals.js').AbstractAttributionManager} am
|
||||||
* @return {{ children: import('../utils/Delta.js').ArrayDelta<Array<YXmlElement|YXmlText|YXmlHook>> }}
|
* @return {{ children: import('../utils/Delta.js').ArrayDelta<Array<YXmlElement|YXmlText|YXmlHook>> }}
|
||||||
*/
|
*/
|
||||||
getContent (am = noAttributionsManager) {
|
getDelta (am = noAttributionsManager) {
|
||||||
const children = typeListGetContent(this, am)
|
const children = typeListGetContent(this, am)
|
||||||
return { children }
|
return { children }
|
||||||
}
|
}
|
||||||
@@ -398,7 +398,7 @@ export class YXmlFragment extends AbstractType {
|
|||||||
* @return {{ children: import('../utils/Delta.js').ArrayDelta<Array<import('./AbstractType.js').YXmlDeepContent>> }}
|
* @return {{ children: import('../utils/Delta.js').ArrayDelta<Array<import('./AbstractType.js').YXmlDeepContent>> }}
|
||||||
*/
|
*/
|
||||||
getContentDeep (am) {
|
getContentDeep (am) {
|
||||||
const { children: origChildren } = this.getContent(am)
|
const { children: origChildren } = this.getDelta(am)
|
||||||
/**
|
/**
|
||||||
* @type {import('../utils/Delta.js').ArrayDelta<Array<import('./AbstractType.js').YXmlDeepContent>>}
|
* @type {import('../utils/Delta.js').ArrayDelta<Array<import('./AbstractType.js').YXmlDeepContent>>}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export class YXmlText extends YText {
|
|||||||
*/
|
*/
|
||||||
clone () {
|
clone () {
|
||||||
const text = new YXmlText()
|
const text = new YXmlText()
|
||||||
text.applyDelta(this.getContent())
|
text.applyDelta(this.getDelta())
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ export class YXmlText extends YText {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toString () {
|
toString () {
|
||||||
return this.getContent().ops.map(dop => {
|
return this.getDelta().ops.map(dop => {
|
||||||
if (dop instanceof delta.InsertStringOp) {
|
if (dop instanceof delta.InsertStringOp) {
|
||||||
const nestedNodes = []
|
const nestedNodes = []
|
||||||
for (const nodeName in dop.attributes) {
|
for (const nodeName in dop.attributes) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import * as object from 'lib0/object'
|
import * as object from 'lib0/object'
|
||||||
|
import * as map from 'lib0/map'
|
||||||
import * as fun from 'lib0/function'
|
import * as fun from 'lib0/function'
|
||||||
import * as traits from 'lib0/traits'
|
import * as traits from 'lib0/traits'
|
||||||
import * as error from 'lib0/error'
|
import * as error from 'lib0/error'
|
||||||
@@ -6,12 +7,14 @@ import * as error from 'lib0/error'
|
|||||||
/**
|
/**
|
||||||
* @template {any} ArrayContent
|
* @template {any} ArrayContent
|
||||||
* @template {object} Embeds
|
* @template {object} Embeds
|
||||||
* @typedef {InsertStringOp|InsertEmbedOp<Embeds>|InsertArrayOp<ArrayContent>|RetainOp|DeleteOp} DeltaOp
|
* @template {Delta|undefined} ModifyingDelta
|
||||||
|
* @typedef {InsertStringOp|InsertEmbedOp<Embeds>|InsertArrayOp<ArrayContent>|RetainOp|DeleteOp|(ModifyingDelta extends undefined ? never : ModifyOp<ModifyingDelta extends undefined ? never : ModifyingDelta>)} DeltaOp
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {object} Embeds
|
* @template {object} Embeds
|
||||||
* @typedef {InsertStringOp|InsertEmbedOp<Embeds>|RetainOp|DeleteOp} TextDeltaOp
|
* @template {Delta|undefined} Modifiers
|
||||||
|
* @typedef {InsertStringOp|InsertEmbedOp<Embeds>|RetainOp|DeleteOp|(Modifiers extends undefined ? never : ModifyOp<Modifiers extends undefined ? never : Modifiers>)} TextDeltaOp
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,7 +35,7 @@ import * as error from 'lib0/error'
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {{ insert: string|object, attributes?: { [key: string]: any }, attribution?: Attribution_ } | { delete: number } | { retain: number, attributes?: { [key:string]: any }, attribution?: Attribution_ }} DeltaJsonOp
|
* @typedef {{ insert: string|object, attributes?: { [key: string]: any }, attribution?: Attribution_ } | { delete: number } | { retain: number, attributes?: { [key:string]: any }, attribution?: Attribution_ } | { modify: object }} DeltaJsonOp
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export class InsertStringOp {
|
export class InsertStringOp {
|
||||||
@@ -193,18 +196,71 @@ export class RetainOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {(TextDelta<any> | ArrayDelta<any>)} Delta
|
* Delta that can be applied on a YType Embed
|
||||||
|
*
|
||||||
|
* @template {Delta} DTypes
|
||||||
|
*/
|
||||||
|
export class ModifyOp {
|
||||||
|
/**
|
||||||
|
* @param {DTypes} delta
|
||||||
|
*/
|
||||||
|
constructor (delta) {
|
||||||
|
this.modify = delta
|
||||||
|
}
|
||||||
|
|
||||||
|
get length () {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {DeltaJsonOp}
|
||||||
|
*/
|
||||||
|
toJSON () {
|
||||||
|
return { modify: this.modify.toJSON() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ModifyOp<any>} other
|
||||||
|
*/
|
||||||
|
[traits.EqualityTraitSymbol] (other) {
|
||||||
|
return this.modify[traits.EqualityTraitSymbol](other.modify)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AbstractDelta {
|
||||||
|
constructor () {
|
||||||
|
this.remote = false
|
||||||
|
/**
|
||||||
|
* @type {any} origin
|
||||||
|
*/
|
||||||
|
this.origin = null
|
||||||
|
this.isDiff = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} _other
|
||||||
|
*/
|
||||||
|
[traits.EqualityTraitSymbol] (_other) {
|
||||||
|
error.methodUnimplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {Delta|undefined} [Modifiers=any]
|
||||||
|
* @typedef {(TextDelta<any,Modifiers> | ArrayDelta<any,Modifiers> | MapDelta<object> | XmlDelta<string,any,any,Modifiers,Modifiers> )} Delta
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {'array' | 'text' | 'custom'} Type
|
* @template {'array' | 'text' | 'custom'} Type
|
||||||
* @template {DeltaOp<any,any>} TDeltaOp
|
* @template {DeltaOp<any,any,Modifiers>} TDeltaOp
|
||||||
|
* @template {Delta|undefined} Modifiers
|
||||||
*/
|
*/
|
||||||
export class AbstractDelta {
|
export class AbstractArrayDelta extends AbstractDelta {
|
||||||
/**
|
/**
|
||||||
* @param {Type} type
|
* @param {Type} type
|
||||||
*/
|
*/
|
||||||
constructor (type) {
|
constructor (type) {
|
||||||
|
super()
|
||||||
this.type = type
|
this.type = type
|
||||||
/**
|
/**
|
||||||
* @type {Array<TDeltaOp>}
|
* @type {Array<TDeltaOp>}
|
||||||
@@ -213,12 +269,12 @@ export class AbstractDelta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {(d:TDeltaOp) => DeltaOp<any,any>} Mapper
|
* @template {(d:TDeltaOp) => DeltaOp<any,any,any>} Mapper
|
||||||
* @param {Mapper} f
|
* @param {Mapper} f
|
||||||
* @return {DeltaBuilder<Type, Mapper extends (d:TDeltaOp) => infer OP ? OP : unknown>}
|
* @return {DeltaBuilder<Type, Mapper extends (d:TDeltaOp) => infer OP ? OP : unknown,Modifiers>}
|
||||||
*/
|
*/
|
||||||
map (f) {
|
map (f) {
|
||||||
const d = /** @type {DeltaBuilder<Type,any>} */ (new /** @type {any} */ (this.constructor)(this.type))
|
const d = /** @type {DeltaBuilder<Type,any,Modifiers>} */ (new /** @type {any} */ (this.constructor)(this.type))
|
||||||
d.ops = this.ops.map(f)
|
d.ops = this.ops.map(f)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
d.lastOp = d.ops[d.ops.length - 1] ?? null
|
d.lastOp = d.ops[d.ops.length - 1] ?? null
|
||||||
@@ -226,20 +282,61 @@ export class AbstractDelta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {(d:TDeltaOp,index:number)=>void} f
|
*
|
||||||
|
* Iterate through the changes. There are two approches to iterate through the changes. The
|
||||||
|
* following examples achieve the same thing:
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* d.forEach((op, index) => {
|
||||||
|
* if (op instanceof delta.InsertArrayOp) {
|
||||||
|
* op.insert
|
||||||
|
* } else if (op instanceof delta.RetainOp ) {
|
||||||
|
* op.retain
|
||||||
|
* } else if (op instanceof delta.DeleteOp) {
|
||||||
|
* op.delete
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* The second approach doesn't require instanceof checks.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* d.forEach(null,
|
||||||
|
* (insertOp, index) => insertOp.insert,
|
||||||
|
* (retainOp, index) => insertOp.retain
|
||||||
|
* (deleteOp, index) => insertOp.delete
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* @param {null|((d:TDeltaOp,index:number)=>void)} f
|
||||||
|
* @param {null|((insertOp: (InsertEmbedOp<any> | InsertStringOp | InsertArrayOp<any>) & TDeltaOp,index:number)=>void)} insertHandler
|
||||||
|
* @param {null|((retainOp:RetainOp,index:number)=>void)} retainHandler
|
||||||
|
* @param {null|((deleteOp:DeleteOp,index:number)=>void)} deleteHandler
|
||||||
|
* @param {null|(Modifiers extends undefined ? null : ((modifyOp:ModifyOp<Modifiers extends undefined ? never : Modifiers>,index:number)=>void))} modifyHandler
|
||||||
*/
|
*/
|
||||||
forEach (f) {
|
forEach (f = null, insertHandler = null, retainHandler = null, deleteHandler = null, modifyHandler = null) {
|
||||||
for (
|
for (
|
||||||
let i = 0, index = 0, op = this.ops[i];
|
let i = 0, index = 0, op = this.ops[i];
|
||||||
i < this.ops.length;
|
i < this.ops.length;
|
||||||
i++, index += op.length, op = this.ops[i]
|
i++, index += op.length, op = this.ops[i]
|
||||||
) {
|
) {
|
||||||
f(op, index)
|
f?.(op, index)
|
||||||
|
switch (op.constructor) {
|
||||||
|
case RetainOp:
|
||||||
|
retainHandler?.(/** @type {RetainOp} */ (op), index)
|
||||||
|
break
|
||||||
|
case DeleteOp:
|
||||||
|
deleteHandler?.(/** @type {DeleteOp} */ (op), index)
|
||||||
|
break
|
||||||
|
case ModifyOp:
|
||||||
|
modifyHandler?.(/** @type {any}) */ (op), index)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
insertHandler?.(/** @type {any} */ (op), index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {AbstractDelta<Type, TDeltaOp>} other
|
* @param {AbstractArrayDelta<Type,TDeltaOp,any>} other
|
||||||
* @return {boolean}
|
* @return {boolean}
|
||||||
*/
|
*/
|
||||||
equals (other) {
|
equals (other) {
|
||||||
@@ -254,13 +351,331 @@ export class AbstractDelta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {AbstractDelta<Type,TDeltaOp>} other
|
* @param {AbstractArrayDelta<Type,TDeltaOp,any>} other
|
||||||
*/
|
*/
|
||||||
[traits.EqualityTraitSymbol] (other) {
|
[traits.EqualityTraitSymbol] (other) {
|
||||||
return fun.equalityDeep(this.ops, other.ops)
|
return fun.equalityDeep(this.ops, other.ops)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {object} Vals
|
||||||
|
* @template {keyof Vals} K
|
||||||
|
* @template {Delta|undefined} Modifiers
|
||||||
|
* @typedef {(change:MapDeltaChange<Vals[K],Modifiers>,key:K) => void} MapDeltaChangeCallback
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template V
|
||||||
|
*/
|
||||||
|
class MapInsertOp {
|
||||||
|
/**
|
||||||
|
* @param {V} value
|
||||||
|
* @param {V|undefined} prevValue
|
||||||
|
* @param {Attribution_?} attribution
|
||||||
|
*/
|
||||||
|
constructor (value, prevValue, attribution) {
|
||||||
|
this.prevValue = prevValue
|
||||||
|
this.attribution = attribution
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
get type () { return 'insert' }
|
||||||
|
|
||||||
|
toJSON () {
|
||||||
|
return {
|
||||||
|
type: this.type,
|
||||||
|
value: this.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template V
|
||||||
|
*/
|
||||||
|
class MapDeleteOp {
|
||||||
|
/**
|
||||||
|
* @param {V|undefined} prevValue
|
||||||
|
* @param {Attribution_?} attribution
|
||||||
|
*/
|
||||||
|
constructor (prevValue, attribution) {
|
||||||
|
this.prevValue = prevValue
|
||||||
|
this.attribution = attribution
|
||||||
|
}
|
||||||
|
|
||||||
|
get value () { return undefined }
|
||||||
|
|
||||||
|
get type () { return 'delete' }
|
||||||
|
|
||||||
|
toJSON () {
|
||||||
|
return {
|
||||||
|
type: 'delete'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {Delta} Modifiers
|
||||||
|
*/
|
||||||
|
class MapModifyOp {
|
||||||
|
/**
|
||||||
|
* @param {Modifiers} delta
|
||||||
|
*/
|
||||||
|
constructor (delta) {
|
||||||
|
this.modify = delta
|
||||||
|
}
|
||||||
|
|
||||||
|
get value () { return undefined }
|
||||||
|
|
||||||
|
get type () { return 'insert' }
|
||||||
|
|
||||||
|
toJSON () {
|
||||||
|
return {
|
||||||
|
type: 'modify',
|
||||||
|
modify: this.modify.toJSON()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template V
|
||||||
|
* @template {Delta|undefined} Modifiers
|
||||||
|
* @typedef {MapInsertOp<V> | MapDeleteOp<V> | (Modifiers extends undefined ? never : MapModifyOp<Modifiers extends undefined ? never : Modifiers>)} MapDeltaChange
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {object} Vals
|
||||||
|
* @template {Delta|undefined} [Modifiers=undefined]
|
||||||
|
*/
|
||||||
|
export class MapDelta extends AbstractDelta {
|
||||||
|
constructor () {
|
||||||
|
super()
|
||||||
|
/**
|
||||||
|
* @type {Map<keyof Vals,MapDeltaChange<Vals[keyof Vals],Modifiers>>}
|
||||||
|
*/
|
||||||
|
this.changes = map.create()
|
||||||
|
/**
|
||||||
|
* @type {Attribution_?}
|
||||||
|
*/
|
||||||
|
this.usedAttribution = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Iterate through the changes. There are two approches to iterate through changes. The
|
||||||
|
* following two examples achieve the same thing:
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* d.forEach((op, index) => {
|
||||||
|
* if (op instanceof delta.InsertArrayOp) {
|
||||||
|
* op.insert
|
||||||
|
* } else if (op instanceof delta.RetainOp ) {
|
||||||
|
* op.retain
|
||||||
|
* } else if (op instanceof delta.DeleteOp) {
|
||||||
|
* op.delete
|
||||||
|
* } else if (op instanceof delta.ModifyOp) {
|
||||||
|
* op.modify
|
||||||
|
* }
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* The second approach doesn't require instanceof checks.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* d.forEach(null,
|
||||||
|
* (insertOp, index) => insertOp.insert,
|
||||||
|
* (retainOp, index) => insertOp.retain
|
||||||
|
* (deleteOp, index) => insertOp.delete
|
||||||
|
* (modifyOp, index) => insertOp.modify
|
||||||
|
* )
|
||||||
|
*
|
||||||
|
* @param {null|((change:MapDeltaChange<Vals[keyof Vals],Modifiers>,key:keyof Vals)=>void)} changeHandler
|
||||||
|
* @param {null|((insertOp:MapInsertOp<Vals[keyof Vals]>,key:keyof Vals)=>void)} insertHandler
|
||||||
|
* @param {null|((deleteOp:MapDeleteOp<Vals[keyof Vals]>,key:keyof Vals)=>void)} deleteHandler
|
||||||
|
* @param {null|((modifyOp:(MapModifyOp<Modifiers extends undefined ? never : Modifiers>),key:keyof Vals)=>void)} modifyHandler
|
||||||
|
*/
|
||||||
|
forEach (changeHandler = null, insertHandler = null, deleteHandler = null, modifyHandler = null) {
|
||||||
|
this.changes.forEach((change, key) => {
|
||||||
|
changeHandler?.(change, key)
|
||||||
|
switch (change.constructor) {
|
||||||
|
case MapDeleteOp:
|
||||||
|
deleteHandler?.(/** @type {MapDeleteOp<Vals[keyof Vals]>} */ (change), key)
|
||||||
|
break
|
||||||
|
case MapInsertOp:
|
||||||
|
insertHandler?.(/** @type {MapInsertOp<Vals[keyof Vals]>} */ (change), key)
|
||||||
|
break
|
||||||
|
case MapModifyOp:
|
||||||
|
modifyHandler?.(/** @type {MapModifyOp<Modifiers extends undefined ? never : Modifiers>} */ (change), key)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {keyof Vals} K
|
||||||
|
*
|
||||||
|
* @param {K} key
|
||||||
|
* @return {MapDeltaChange<Vals[K],Modifiers> | undefined}
|
||||||
|
*/
|
||||||
|
get (key) {
|
||||||
|
return /** @type {MapDeltaChange<Vals[K],Modifiers> | undefined} */ (this.changes.get(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {keyof Vals} key
|
||||||
|
*/
|
||||||
|
has (key) {
|
||||||
|
return this.changes.has(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {MapDelta<any>} other
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
equals (other) {
|
||||||
|
return this[traits.EqualityTraitSymbol](other)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {object}
|
||||||
|
*/
|
||||||
|
toJSON () {
|
||||||
|
/**
|
||||||
|
* @type {any}
|
||||||
|
*/
|
||||||
|
const changes = {}
|
||||||
|
this.changes.forEach((change, key) => {
|
||||||
|
changes[key] = change.toJSON()
|
||||||
|
})
|
||||||
|
return changes
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preferred way to iterate through changes.
|
||||||
|
*
|
||||||
|
* @return {IterableIterator<{ [K in keyof Vals]: [K, MapDeltaChange<Vals[K],Modifiers>] }[keyof Vals]>}
|
||||||
|
*/
|
||||||
|
[Symbol.iterator] () {
|
||||||
|
// @ts-ignore
|
||||||
|
return this.changes.entries()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {MapDelta<any>} other
|
||||||
|
*/
|
||||||
|
[traits.EqualityTraitSymbol] (other) {
|
||||||
|
return fun.equalityDeep(this.changes, other.changes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {MapDelta<Vals,Modifiers>}
|
||||||
|
*/
|
||||||
|
done () {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {string|undefined} NodeName
|
||||||
|
* @template Children
|
||||||
|
* @template {object} Attrs
|
||||||
|
* @template {Delta|undefined} [ChildModifiers=undefined]
|
||||||
|
* @template {Delta|undefined} [AttrModifiers=undefined]
|
||||||
|
* @template {'done'|'mutable'} [Done='mutable']
|
||||||
|
*/
|
||||||
|
export class XmlDelta extends AbstractDelta {
|
||||||
|
/**
|
||||||
|
* @param {NodeName} nodeName
|
||||||
|
* @param {ArrayDelta<Children,ChildModifiers>} children
|
||||||
|
* @param {MapDelta<Attrs,AttrModifiers>} attributes
|
||||||
|
*/
|
||||||
|
constructor (nodeName, children = createArrayDelta(), attributes = /** @type {any} */ (createMapDelta())) {
|
||||||
|
super()
|
||||||
|
this.nodeName = nodeName
|
||||||
|
/**
|
||||||
|
* @type {ArrayDelta<Children,ChildModifiers>}
|
||||||
|
*/
|
||||||
|
this.children = children
|
||||||
|
/**
|
||||||
|
* @type {Done extends 'mutable' ? MapDeltaBuilder<Attrs> : MapDelta<Attrs>}
|
||||||
|
*/
|
||||||
|
this.attributes = /** @type {any} */ (attributes)
|
||||||
|
}
|
||||||
|
|
||||||
|
toJSON () {
|
||||||
|
return {
|
||||||
|
nodeName: this.nodeName,
|
||||||
|
children: this.children.toJSON(),
|
||||||
|
attributes: this.attributes.toJSON()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {XmlDelta<Children, Attrs, ChildModifiers, AttrModifiers, 'done'>}
|
||||||
|
*/
|
||||||
|
done () {
|
||||||
|
this.children.done()
|
||||||
|
this.attributes.done()
|
||||||
|
return /** @type {any} */ (this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string|undefined} nodeName
|
||||||
|
*/
|
||||||
|
export const createXmlDelta = (nodeName = undefined) => new XmlDelta(nodeName)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {object} Vals
|
||||||
|
* @template {Delta|undefined} [Modifiers=undefined]
|
||||||
|
* @extends MapDelta<Vals,Modifiers>
|
||||||
|
*/
|
||||||
|
export class MapDeltaBuilder extends MapDelta {
|
||||||
|
/**
|
||||||
|
* @template {keyof Vals} K
|
||||||
|
* @param {K} key
|
||||||
|
* @param {Modifiers} delta
|
||||||
|
*/
|
||||||
|
modify (key, delta) {
|
||||||
|
this.changes.set(key, /** @type {any} */ ({ type: 'modify', delta }))
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {keyof Vals} K
|
||||||
|
* @param {K} key
|
||||||
|
* @param {Vals[K]} newVal
|
||||||
|
* @param {Vals[K]|undefined} prevValue
|
||||||
|
* @param {Attribution_?} attribution
|
||||||
|
*/
|
||||||
|
set (key, newVal, prevValue = undefined, attribution = null) {
|
||||||
|
const mergedAttribution = mergeAttrs(this.usedAttribution, attribution)
|
||||||
|
this.changes.set(key, new MapInsertOp(newVal, prevValue, mergedAttribution))
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template {keyof Vals} K
|
||||||
|
* @param {K} key
|
||||||
|
* @param {Vals[K]|undefined} prevValue
|
||||||
|
* @param {Attribution_?} attribution
|
||||||
|
*/
|
||||||
|
delete (key, prevValue = undefined, attribution = null) {
|
||||||
|
const mergedAttribution = mergeAttrs(this.usedAttribution, attribution)
|
||||||
|
this.changes.set(key, new MapDeleteOp(prevValue, mergedAttribution))
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Attribution_?} attribution
|
||||||
|
*/
|
||||||
|
useAttribution (attribution) {
|
||||||
|
this.usedAttribution = attribution
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createMapDelta = () => new MapDeltaBuilder()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to merge attribution and attributes. The latter input "wins".
|
* Helper function to merge attribution and attributes. The latter input "wins".
|
||||||
*
|
*
|
||||||
@@ -272,10 +687,11 @@ const mergeAttrs = (a, b) => object.isEmpty(a) ? b : (object.isEmpty(b) ? a : ob
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {'array' | 'text' | 'custom'} Type
|
* @template {'array' | 'text' | 'custom'} Type
|
||||||
* @template {DeltaOp<any,any>} TDeltaOp
|
* @template {DeltaOp<any,any,Modifiers>} TDeltaOp
|
||||||
* @extends AbstractDelta<Type,TDeltaOp>
|
* @template {Delta|undefined} Modifiers
|
||||||
|
* @extends AbstractArrayDelta<Type,TDeltaOp,Modifiers>
|
||||||
*/
|
*/
|
||||||
export class DeltaBuilder extends AbstractDelta {
|
export class DeltaBuilder extends AbstractArrayDelta {
|
||||||
/**
|
/**
|
||||||
* @param {Type} type
|
* @param {Type} type
|
||||||
*/
|
*/
|
||||||
@@ -420,7 +836,8 @@ export class DeltaBuilder extends AbstractDelta {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {any} ArrayContent
|
* @template {any} ArrayContent
|
||||||
* @extends DeltaBuilder<'array', ArrayDeltaOp<ArrayContent>>>
|
* @template {Delta|undefined} Modifiers
|
||||||
|
* @extends DeltaBuilder<'array', ArrayDeltaOp<ArrayContent>,Modifiers>
|
||||||
*/
|
*/
|
||||||
export class ArrayDelta extends DeltaBuilder {
|
export class ArrayDelta extends DeltaBuilder {
|
||||||
constructor () {
|
constructor () {
|
||||||
@@ -430,7 +847,8 @@ export class ArrayDelta extends DeltaBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {object} Embeds
|
* @template {object} Embeds
|
||||||
* @extends DeltaBuilder<'text',TextDeltaOp<Embeds>>
|
* @template {Delta|undefined} [Modifiers=undefined]
|
||||||
|
* @extends DeltaBuilder<'text',TextDeltaOp<Embeds,Modifiers>,Modifiers>
|
||||||
*/
|
*/
|
||||||
export class TextDelta extends DeltaBuilder {
|
export class TextDelta extends DeltaBuilder {
|
||||||
constructor () {
|
constructor () {
|
||||||
@@ -440,24 +858,28 @@ export class TextDelta extends DeltaBuilder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {'text'|'array'|'custom'} Type
|
* @template {'text'|'array'|'custom'} Type
|
||||||
* @template {DeltaOp<any,any>} DeltaOps
|
* @template {DeltaOp<any,any,Modifiers>} DeltaOps
|
||||||
* @typedef {AbstractDelta<Type, DeltaOps>} DeltaReadonly
|
* @template {Delta|undefined} Modifiers
|
||||||
|
* @typedef {AbstractArrayDelta<Type,DeltaOps,Modifiers>} DeltaReadonly
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {object} Embeds
|
* @template {object} Embeds
|
||||||
* @typedef {DeltaReadonly<'text',TextDeltaOp<Embeds>>} TextDeltaReadonly
|
* @template {Delta|undefined} Modifiers
|
||||||
|
* @typedef {DeltaReadonly<'text',TextDeltaOp<Embeds,Modifiers>,Modifiers>} TextDeltaReadonly
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {object} Embeds
|
* @template {object} Embeds
|
||||||
* @return {TextDelta<Embeds>}
|
* @template {Delta|undefined} Modifiers
|
||||||
|
* @return {TextDelta<Embeds,Modifiers>}
|
||||||
*/
|
*/
|
||||||
export const createTextDelta = () => new TextDelta()
|
export const createTextDelta = () => new TextDelta()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template [V=any]
|
* @template [V=any]
|
||||||
* @return {ArrayDelta<V>}
|
* @template {Delta|undefined} [Modifiers=undefined]
|
||||||
|
* @return {ArrayDelta<V,Modifiers>}
|
||||||
*/
|
*/
|
||||||
export const createArrayDelta = () => new ArrayDelta()
|
export const createArrayDelta = () => new ArrayDelta()
|
||||||
|
|
||||||
|
|||||||
@@ -41,5 +41,5 @@ export const testTextDecodingCompatibilityV1 = _tc => {
|
|||||||
const oldVal = [{"insert":"1306rup"},{"insert":"uj","attributes":{"italic":true,"color":"#888"}},{"insert":"ikkcjnrcpsckw1319bccgkp\n"},{"insert":"\n1131","attributes":{"bold":true}},{"insert":"1326rpcznqahopcrtd","attributes":{"italic":true}},{"insert":"3axhkthhu","attributes":{"bold":true}},{"insert":"28"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"9"},{"insert":"04ku","attributes":{"italic":true}},{"insert":"1323nucvxsqlznwlfavmpc\nu"},{"insert":"tc","attributes":{"italic":true}},{"insert":"je1318jwskjabdndrdlmjae\n1293tj\nj1292qrmf"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"k\nuf"},{"insert":"14hs","attributes":{"italic":true}},{"insert":"13dccxdyxg"},{"insert":"zc","attributes":{"italic":true,"color":"#888"}},{"insert":"apo"},{"insert":"tn","attributes":{"bold":true}},{"insert":"r"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"gn\n"},{"insert":"z","attributes":{"italic":true}},{"insert":"\n121"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"291311kk9zjznywohpx"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"cnbrcaq\n"},{"insert":"1","attributes":{"italic":true,"color":"#888"}},{"insert":"1310g"},{"insert":"ws","attributes":{"italic":true,"color":"#888"}},{"insert":"hxwych"},{"insert":"kq","attributes":{"italic":true}},{"insert":"sdru1320cohbvcrkrpjngdoc\njqic\n"},{"insert":"2","attributes":{"italic":true,"color":"#888"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"90n1297zm"},{"insert":"v1309zlgvjx","attributes":{"bold":true}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"g","attributes":{"bold":true}},{"insert":"1314pycavu","attributes":{"italic":true,"color":"#888"}},{"insert":"pkzqcj"},{"insert":"sa","attributes":{"italic":true,"color":"#888"}},{"insert":"sjy\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"xr\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"1"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"1295qfrvlyfap201312qrwt"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"b1322rnbaokorixenvp\nrxq"},{"insert":"j","attributes":{"italic":true}},{"insert":"x","attributes":{"italic":true,"color":"#888"}},{"insert":"15mziwabzkrrmscvdovao\n0","attributes":{"italic":true}},{"insert":"hx","attributes":{"italic":true,"bold":true}},{"insert":"ojeetrjhxkr13031317pfcyhksrkpkt\nuhv1","attributes":{"italic":true}},{"insert":"32","attributes":{"italic":true,"color":"#888"}},{"insert":"4rorywthq1325iodbzizxhmlibvpyrxmq\n\nganln\nqne\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"dvf"},{"insert":"ac","attributes":{"bold":true}},{"insert":"1302xciwa"},{"insert":"1305rl","attributes":{"bold":true}},{"insert":"08\n"},{"insert":"eyk","attributes":{"bold":true}},{"insert":"y1321apgivydqsjfsehhezukiqtt1307tvjiejlh"},{"insert":"1316zlpkmctoqomgfthbpg","attributes":{"bold":true}},{"insert":"gv"},{"insert":"lb","attributes":{"bold":true}},{"insert":"f\nhntk\njv1uu\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}}]
|
const oldVal = [{"insert":"1306rup"},{"insert":"uj","attributes":{"italic":true,"color":"#888"}},{"insert":"ikkcjnrcpsckw1319bccgkp\n"},{"insert":"\n1131","attributes":{"bold":true}},{"insert":"1326rpcznqahopcrtd","attributes":{"italic":true}},{"insert":"3axhkthhu","attributes":{"bold":true}},{"insert":"28"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"9"},{"insert":"04ku","attributes":{"italic":true}},{"insert":"1323nucvxsqlznwlfavmpc\nu"},{"insert":"tc","attributes":{"italic":true}},{"insert":"je1318jwskjabdndrdlmjae\n1293tj\nj1292qrmf"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"k\nuf"},{"insert":"14hs","attributes":{"italic":true}},{"insert":"13dccxdyxg"},{"insert":"zc","attributes":{"italic":true,"color":"#888"}},{"insert":"apo"},{"insert":"tn","attributes":{"bold":true}},{"insert":"r"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"gn\n"},{"insert":"z","attributes":{"italic":true}},{"insert":"\n121"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"291311kk9zjznywohpx"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"cnbrcaq\n"},{"insert":"1","attributes":{"italic":true,"color":"#888"}},{"insert":"1310g"},{"insert":"ws","attributes":{"italic":true,"color":"#888"}},{"insert":"hxwych"},{"insert":"kq","attributes":{"italic":true}},{"insert":"sdru1320cohbvcrkrpjngdoc\njqic\n"},{"insert":"2","attributes":{"italic":true,"color":"#888"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"90n1297zm"},{"insert":"v1309zlgvjx","attributes":{"bold":true}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"g","attributes":{"bold":true}},{"insert":"1314pycavu","attributes":{"italic":true,"color":"#888"}},{"insert":"pkzqcj"},{"insert":"sa","attributes":{"italic":true,"color":"#888"}},{"insert":"sjy\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"xr\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"1"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"1295qfrvlyfap201312qrwt"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"b1322rnbaokorixenvp\nrxq"},{"insert":"j","attributes":{"italic":true}},{"insert":"x","attributes":{"italic":true,"color":"#888"}},{"insert":"15mziwabzkrrmscvdovao\n0","attributes":{"italic":true}},{"insert":"hx","attributes":{"italic":true,"bold":true}},{"insert":"ojeetrjhxkr13031317pfcyhksrkpkt\nuhv1","attributes":{"italic":true}},{"insert":"32","attributes":{"italic":true,"color":"#888"}},{"insert":"4rorywthq1325iodbzizxhmlibvpyrxmq\n\nganln\nqne\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}},{"insert":"dvf"},{"insert":"ac","attributes":{"bold":true}},{"insert":"1302xciwa"},{"insert":"1305rl","attributes":{"bold":true}},{"insert":"08\n"},{"insert":"eyk","attributes":{"bold":true}},{"insert":"y1321apgivydqsjfsehhezukiqtt1307tvjiejlh"},{"insert":"1316zlpkmctoqomgfthbpg","attributes":{"bold":true}},{"insert":"gv"},{"insert":"lb","attributes":{"bold":true}},{"insert":"f\nhntk\njv1uu\n"},{"insert":{"image":"https://user-images.githubusercontent.com/5553757/48975307-61efb100-f06d-11e8-9177-ee895e5916e5.png"}}]
|
||||||
const doc = new Y.Doc()
|
const doc = new Y.Doc()
|
||||||
Y.applyUpdate(doc, buffer.fromBase64(oldDoc))
|
Y.applyUpdate(doc, buffer.fromBase64(oldDoc))
|
||||||
t.compare(doc.getText('text').getContent().toJSON(), oldVal)
|
t.compare(doc.getText('text').getDelta().toJSON(), oldVal)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import * as t from 'lib0/testing'
|
import * as t from 'lib0/testing'
|
||||||
import * as delta from '../src/utils/Delta.js'
|
import * as delta from '../src/utils/Delta.js'
|
||||||
|
import * as Y from 'yjs'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {t.TestCase} _tc
|
* @param {t.TestCase} _tc
|
||||||
@@ -77,3 +78,91 @@ export const testUseAttribution = _tc => {
|
|||||||
.done()
|
.done()
|
||||||
t.compare(d, d2)
|
t.compare(d, d2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {t.TestCase} _tc
|
||||||
|
*/
|
||||||
|
export const testMapDelta = _tc => {
|
||||||
|
const d = /** @type {delta.MapDeltaBuilder<{ key: string, v: number, over: string }>} */ (delta.createMapDelta())
|
||||||
|
d.set('key', 'value')
|
||||||
|
.useAttribution({ delete: ['me'] })
|
||||||
|
.delete('v', 94)
|
||||||
|
.useAttribution(null)
|
||||||
|
.set('over', 'writeme', 'i existed before')
|
||||||
|
.set('over', 'andout')
|
||||||
|
.done()
|
||||||
|
t.compare(d.toJSON(), {
|
||||||
|
key: { type: 'insert', value: 'value', prevValue: undefined, attribution: null },
|
||||||
|
v: { type: 'delete', value: undefined, prevValue: 94, attribution: { delete: ['me'] } },
|
||||||
|
over: { type: 'insert', value: 'andout', prevValue: 'i existed before', attribution: null }
|
||||||
|
})
|
||||||
|
t.compare(d.origin, null)
|
||||||
|
t.compare(d.remote, false)
|
||||||
|
t.compare(d.isDiff, true)
|
||||||
|
d.forEach((change, key) => {
|
||||||
|
if (key === 'v') {
|
||||||
|
t.assert(d.get(key)?.prevValue === 94) // should know that value is number
|
||||||
|
t.assert(change.prevValue === 94)
|
||||||
|
} else if (key === 'key') {
|
||||||
|
t.assert(d.get(key)?.value === 'value') // show know that value is a string
|
||||||
|
t.assert(change.value === 'value')
|
||||||
|
} else if (key === 'over') {
|
||||||
|
t.assert(change.value === 'andout')
|
||||||
|
} else {
|
||||||
|
throw new Error()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
for (const [key, change] of d) {
|
||||||
|
if (key === 'v') {
|
||||||
|
t.assert(d.get(key)?.prevValue === 94)
|
||||||
|
t.assert(change.prevValue === 94) // should know that value is number
|
||||||
|
} else if (key === 'key') {
|
||||||
|
t.assert(change.value === 'value') // should know that value is number
|
||||||
|
} else if (key === 'over') {
|
||||||
|
t.assert(change.value === 'andout')
|
||||||
|
} else {
|
||||||
|
throw new Error()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {t.TestCase} _tc
|
||||||
|
*/
|
||||||
|
export const testXmlDelta = _tc => {
|
||||||
|
const d = /** @type {delta.XmlDelta<string, string, { a: 1 }>} */ (delta.createXmlDelta())
|
||||||
|
d.children.insert(['hi'])
|
||||||
|
d.attributes.set('a', 1)
|
||||||
|
d.attributes.delete('a', 1)
|
||||||
|
/**
|
||||||
|
* @type {Array<Array<string>| number>}
|
||||||
|
*/
|
||||||
|
const arr = []
|
||||||
|
d.children.forEach(
|
||||||
|
(op, index) => {
|
||||||
|
if (op instanceof delta.InsertArrayOp) {
|
||||||
|
arr.push(op.insert, index)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(op, index) => {
|
||||||
|
arr.push(op.insert, index)
|
||||||
|
},
|
||||||
|
(op, index) => {
|
||||||
|
arr.push(op.retain)
|
||||||
|
},
|
||||||
|
(op, index) => {
|
||||||
|
arr.push(op.delete)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
t.compare(arr, [['hi'], 0, ['hi'], 0])
|
||||||
|
const x = d.done()
|
||||||
|
console.log(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {t.TestCase} tc
|
||||||
|
*/
|
||||||
|
export const testTextModifyingDelta = tc => {
|
||||||
|
const d = /** @type {delta.TextDelta<Y.Map<any>|Y.Array<any>>} */ (delta.createTextDelta()).insert('hi').insert(new Y.Map()).done()
|
||||||
|
console.log(d)
|
||||||
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export const testInconsistentFormat = () => {
|
|||||||
const content = /** @type {Y.XmlText} */ (ydoc.get('text', Y.XmlText))
|
const content = /** @type {Y.XmlText} */ (ydoc.get('text', Y.XmlText))
|
||||||
content.format(0, 6, { bold: null })
|
content.format(0, 6, { bold: null })
|
||||||
content.format(6, 4, { type: 'text' })
|
content.format(6, 4, { type: 'text' })
|
||||||
t.compare(content.getContent(), delta.fromJSON([
|
t.compare(content.getDelta(), delta.fromJSON([
|
||||||
{
|
{
|
||||||
attributes: { type: 'text' },
|
attributes: { type: 'text' },
|
||||||
insert: 'Merge Test'
|
insert: 'Merge Test'
|
||||||
@@ -94,11 +94,11 @@ export const testUndoText = tc => {
|
|||||||
t.assert(text0.toString() === 'bcxyz')
|
t.assert(text0.toString() === 'bcxyz')
|
||||||
// test marks
|
// test marks
|
||||||
text0.format(1, 3, { bold: true })
|
text0.format(1, 3, { bold: true })
|
||||||
t.compare(text0.getContent(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }]))
|
t.compare(text0.getDelta(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }]))
|
||||||
undoManager.undo()
|
undoManager.undo()
|
||||||
t.compare(text0.getContent(), delta.fromJSON([{ insert: 'bcxyz' }]))
|
t.compare(text0.getDelta(), delta.fromJSON([{ insert: 'bcxyz' }]))
|
||||||
undoManager.redo()
|
undoManager.redo()
|
||||||
t.compare(text0.getContent(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }]))
|
t.compare(text0.getDelta(), delta.fromJSON([{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }]))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -694,8 +694,8 @@ export const testUndoDeleteTextFormat = _tc => {
|
|||||||
},
|
},
|
||||||
{ insert: ' off the shoulder of Orion.' }
|
{ insert: ' off the shoulder of Orion.' }
|
||||||
])
|
])
|
||||||
t.compare(text.getContent(), expect)
|
t.compare(text.getDelta(), expect)
|
||||||
t.compare(text2.getContent(), expect)
|
t.compare(text2.getDelta(), expect)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ export const testKeyEncoding = tc => {
|
|||||||
const update = Y.encodeStateAsUpdateV2(users[0])
|
const update = Y.encodeStateAsUpdateV2(users[0])
|
||||||
Y.applyUpdateV2(users[1], update)
|
Y.applyUpdateV2(users[1], update)
|
||||||
|
|
||||||
t.compare(text1.getContent().toJSON(), [{ insert: 'c', attributes: { italic: true } }, { insert: 'b' }, { insert: 'a', attributes: { italic: true } }])
|
t.compare(text1.getDelta().toJSON(), [{ insert: 'c', attributes: { italic: true } }, { insert: 'b' }, { insert: 'a', attributes: { italic: true } }])
|
||||||
|
|
||||||
compare(users)
|
compare(users)
|
||||||
}
|
}
|
||||||
@@ -330,7 +330,7 @@ export const testObfuscateUpdates = _tc => {
|
|||||||
const omap = odoc.getMap('map')
|
const omap = odoc.getMap('map')
|
||||||
const oarray = odoc.getArray('array')
|
const oarray = odoc.getArray('array')
|
||||||
// test ytext
|
// test ytext
|
||||||
const delta = /** @type {Array<any>} */ (otext.getContent().toJSON())
|
const delta = /** @type {Array<any>} */ (otext.getDelta().toJSON())
|
||||||
t.assert(delta.length === 2)
|
t.assert(delta.length === 2)
|
||||||
t.assert(delta[0].insert !== 'text' && delta[0].insert.length === 4)
|
t.assert(delta[0].insert !== 'text' && delta[0].insert.length === 4)
|
||||||
t.assert(object.length(delta[0].attributes) === 1)
|
t.assert(object.length(delta[0].attributes) === 1)
|
||||||
|
|||||||
@@ -529,7 +529,7 @@ export const testAttributedContent = _tc => {
|
|||||||
yarray.insert(1, [42])
|
yarray.insert(1, [42])
|
||||||
})
|
})
|
||||||
const 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: [] })
|
||||||
const attributedContent = yarray.getContent(attributionManager)
|
const attributedContent = yarray.getDelta(attributionManager)
|
||||||
console.log(attributedContent.toJSON())
|
console.log(attributedContent.toJSON())
|
||||||
t.assert(attributedContent.equals(expectedContent))
|
t.assert(attributedContent.equals(expectedContent))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -631,21 +631,21 @@ export const testAttributedContent = _tc => {
|
|||||||
t.group('initial value', () => {
|
t.group('initial value', () => {
|
||||||
ymap.set('test', 42)
|
ymap.set('test', 42)
|
||||||
const expectedContent = { test: { prevValue: undefined, value: 42, attribution: { insert: [] } } }
|
const expectedContent = { test: { prevValue: undefined, value: 42, attribution: { insert: [] } } }
|
||||||
const attributedContent = ymap.getContent(attributionManager)
|
const attributedContent = ymap.getDelta(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')
|
||||||
const expectedContent = { test: { prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } } }
|
const expectedContent = { test: { prevValue: 42, value: 'fourtytwo', attribution: { insert: [] } } }
|
||||||
const attributedContent = ymap.getContent(attributionManager)
|
const attributedContent = ymap.getDelta(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')
|
||||||
const expectedContent = { test: { prevValue: 'fourtytwo', value: undefined, attribution: { delete: [] } } }
|
const expectedContent = { test: { prevValue: 'fourtytwo', value: undefined, attribution: { delete: [] } } }
|
||||||
const attributedContent = ymap.getContent(attributionManager)
|
const attributedContent = ymap.getDelta(attributionManager)
|
||||||
console.log(attributedContent)
|
console.log(attributedContent)
|
||||||
t.compare(expectedContent, attributedContent)
|
t.compare(expectedContent, attributedContent)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ export const testDeltaBug = _tc => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
ytext.applyDelta(addingList)
|
ytext.applyDelta(addingList)
|
||||||
const result = ytext.getContent()
|
const result = ytext.getDelta()
|
||||||
const expectedResult = delta.createTextDelta()
|
const expectedResult = delta.createTextDelta()
|
||||||
.insert('\n', { 'block-id': 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087' })
|
.insert('\n', { 'block-id': 'block-28eea923-9cbb-4b6f-a950-cf7fd82bc087' })
|
||||||
.insert('\n\n\n', { 'table-col': { width: '150' } })
|
.insert('\n\n\n', { 'table-col': { width: '150' } })
|
||||||
@@ -1589,7 +1589,7 @@ export const testDeltaBug2 = _tc => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
ytext.applyDelta(changeEvent)
|
ytext.applyDelta(changeEvent)
|
||||||
const delta = ytext.getContent()
|
const delta = ytext.getDelta()
|
||||||
t.compare(delta.ops[40].toJSON(), {
|
t.compare(delta.ops[40].toJSON(), {
|
||||||
insert: '\n',
|
insert: '\n',
|
||||||
attributes: {
|
attributes: {
|
||||||
@@ -1670,29 +1670,29 @@ export const testBasicFormat = tc => {
|
|||||||
})
|
})
|
||||||
text0.insert(0, 'abc', { bold: true })
|
text0.insert(0, 'abc', { bold: true })
|
||||||
t.assert(text0.toString() === 'abc', 'Basic insert with attributes works')
|
t.assert(text0.toString() === 'abc', 'Basic insert with attributes works')
|
||||||
t.compare(text0.getContent(), delta.createTextDelta().insert('abc', { bold: true }).done())
|
t.compare(text0.getDelta(), delta.createTextDelta().insert('abc', { bold: true }).done())
|
||||||
t.compare(eventDelta, delta.createTextDelta().insert('abc', { bold: true }))
|
t.compare(eventDelta, delta.createTextDelta().insert('abc', { bold: true }))
|
||||||
text0.delete(0, 1)
|
text0.delete(0, 1)
|
||||||
t.assert(text0.toString() === 'bc', 'Basic delete on formatted works (position 0)')
|
t.assert(text0.toString() === 'bc', 'Basic delete on formatted works (position 0)')
|
||||||
t.compare(text0.getContent(), delta.createTextDelta().insert('bc', { bold: true }))
|
t.compare(text0.getDelta(), delta.createTextDelta().insert('bc', { bold: true }))
|
||||||
t.compare(eventDelta, delta.createTextDelta().delete(1))
|
t.compare(eventDelta, delta.createTextDelta().delete(1))
|
||||||
text0.delete(1, 1)
|
text0.delete(1, 1)
|
||||||
t.assert(text0.toString() === 'b', 'Basic delete works (position 1)')
|
t.assert(text0.toString() === 'b', 'Basic delete works (position 1)')
|
||||||
t.compare(text0.getContent(), delta.createTextDelta().insert('b', { bold: true }))
|
t.compare(text0.getDelta(), delta.createTextDelta().insert('b', { bold: true }))
|
||||||
t.compare(eventDelta, delta.createTextDelta().retain(1).delete(1))
|
t.compare(eventDelta, delta.createTextDelta().retain(1).delete(1))
|
||||||
text0.insert(0, 'z', { bold: true })
|
text0.insert(0, 'z', { bold: true })
|
||||||
t.assert(text0.toString() === 'zb')
|
t.assert(text0.toString() === 'zb')
|
||||||
t.compare(text0.getContent(), delta.createTextDelta().insert('zb', { bold: true }))
|
t.compare(text0.getDelta(), delta.createTextDelta().insert('zb', { bold: true }))
|
||||||
t.compare(eventDelta, delta.createTextDelta().insert('z', { bold: true }))
|
t.compare(eventDelta, delta.createTextDelta().insert('z', { bold: true }))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
t.assert(text0._start.right.right.right.content.str === 'b', 'Does not insert duplicate attribute marker')
|
t.assert(text0._start.right.right.right.content.str === 'b', 'Does not insert duplicate attribute marker')
|
||||||
text0.insert(0, 'y')
|
text0.insert(0, 'y')
|
||||||
t.assert(text0.toString() === 'yzb')
|
t.assert(text0.toString() === 'yzb')
|
||||||
t.compare(text0.getContent(), delta.createTextDelta().insert('y').insert('zb', { bold: true }))
|
t.compare(text0.getDelta(), delta.createTextDelta().insert('y').insert('zb', { bold: true }))
|
||||||
t.compare(eventDelta, delta.createTextDelta().insert('y'))
|
t.compare(eventDelta, delta.createTextDelta().insert('y'))
|
||||||
text0.format(0, 2, { bold: null })
|
text0.format(0, 2, { bold: null })
|
||||||
t.assert(text0.toString() === 'yzb')
|
t.assert(text0.toString() === 'yzb')
|
||||||
t.compare(text0.getContent(), delta.createTextDelta().insert('yz').insert('b', { bold: true }))
|
t.compare(text0.getDelta(), delta.createTextDelta().insert('yz').insert('b', { bold: true }))
|
||||||
t.compare(eventDelta, delta.createTextDelta().retain(1).retain(1, { bold: null }))
|
t.compare(eventDelta, delta.createTextDelta().retain(1).retain(1, { bold: null }))
|
||||||
compare(users)
|
compare(users)
|
||||||
}
|
}
|
||||||
@@ -1707,13 +1707,13 @@ export const testFalsyFormats = tc => {
|
|||||||
delta = event.delta.toJSON()
|
delta = event.delta.toJSON()
|
||||||
})
|
})
|
||||||
text0.insert(0, 'abcde', { falsy: false })
|
text0.insert(0, 'abcde', { falsy: false })
|
||||||
t.compare(text0.getContent().toJSON(), [{ insert: 'abcde', attributes: { falsy: false } }])
|
t.compare(text0.getDelta().toJSON(), [{ insert: 'abcde', attributes: { falsy: false } }])
|
||||||
t.compare(delta, [{ insert: 'abcde', attributes: { falsy: false } }])
|
t.compare(delta, [{ insert: 'abcde', attributes: { falsy: false } }])
|
||||||
text0.format(1, 3, { falsy: true })
|
text0.format(1, 3, { falsy: true })
|
||||||
t.compare(text0.getContent().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'bcd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }])
|
t.compare(text0.getDelta().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'bcd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }])
|
||||||
t.compare(delta, [{ retain: 1 }, { retain: 3, attributes: { falsy: true } }])
|
t.compare(delta, [{ retain: 1 }, { retain: 3, attributes: { falsy: true } }])
|
||||||
text0.format(2, 1, { falsy: false })
|
text0.format(2, 1, { falsy: false })
|
||||||
t.compare(text0.getContent().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'b', attributes: { falsy: true } }, { insert: 'c', attributes: { falsy: false } }, { insert: 'd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }])
|
t.compare(text0.getDelta().toJSON(), [{ insert: 'a', attributes: { falsy: false } }, { insert: 'b', attributes: { falsy: true } }, { insert: 'c', attributes: { falsy: false } }, { insert: 'd', attributes: { falsy: true } }, { insert: 'e', attributes: { falsy: false } }])
|
||||||
t.compare(delta, [{ retain: 2 }, { retain: 1, attributes: { falsy: false } }])
|
t.compare(delta, [{ retain: 2 }, { retain: 1, attributes: { falsy: false } }])
|
||||||
compare(users)
|
compare(users)
|
||||||
}
|
}
|
||||||
@@ -1732,7 +1732,7 @@ export const testMultilineFormat = _tc => {
|
|||||||
{ retain: 1 }, // newline character
|
{ retain: 1 }, // newline character
|
||||||
{ retain: 10, attributes: { bold: true } }
|
{ retain: 10, attributes: { bold: true } }
|
||||||
])
|
])
|
||||||
t.compare(testText.getContent().toJSON(), [
|
t.compare(testText.getDelta().toJSON(), [
|
||||||
{ insert: 'Test', attributes: { bold: true } },
|
{ insert: 'Test', attributes: { bold: true } },
|
||||||
{ insert: '\n' },
|
{ insert: '\n' },
|
||||||
{ insert: 'Multi-line', attributes: { bold: true } },
|
{ insert: 'Multi-line', attributes: { bold: true } },
|
||||||
@@ -1753,7 +1753,7 @@ export const testNotMergeEmptyLinesFormat = _tc => {
|
|||||||
{ insert: '\nText' },
|
{ insert: '\nText' },
|
||||||
{ insert: '\n', attributes: { title: true } }
|
{ insert: '\n', attributes: { title: true } }
|
||||||
])
|
])
|
||||||
t.compare(testText.getContent().toJSON(), [
|
t.compare(testText.getDelta().toJSON(), [
|
||||||
{ insert: 'Text' },
|
{ insert: 'Text' },
|
||||||
{ insert: '\n', attributes: { title: true } },
|
{ insert: '\n', attributes: { title: true } },
|
||||||
{ insert: '\nText' },
|
{ insert: '\nText' },
|
||||||
@@ -1777,7 +1777,7 @@ export const testPreserveAttributesThroughDelete = _tc => {
|
|||||||
{ delete: 1 },
|
{ delete: 1 },
|
||||||
{ retain: 1, attributes: { title: true } }
|
{ retain: 1, attributes: { title: true } }
|
||||||
])
|
])
|
||||||
t.compare(testText.getContent().toJSON(), [
|
t.compare(testText.getDelta().toJSON(), [
|
||||||
{ insert: 'Text' },
|
{ insert: 'Text' },
|
||||||
{ insert: '\n', attributes: { title: true } }
|
{ insert: '\n', attributes: { title: true } }
|
||||||
])
|
])
|
||||||
@@ -1791,7 +1791,7 @@ export const testGetDeltaWithEmbeds = tc => {
|
|||||||
text0.applyDelta([{
|
text0.applyDelta([{
|
||||||
insert: { linebreak: 's' }
|
insert: { linebreak: 's' }
|
||||||
}])
|
}])
|
||||||
t.compare(text0.getContent().toJSON(), [{
|
t.compare(text0.getDelta().toJSON(), [{
|
||||||
insert: { linebreak: 's' }
|
insert: { linebreak: 's' }
|
||||||
}])
|
}])
|
||||||
}
|
}
|
||||||
@@ -1804,7 +1804,7 @@ export const testTypesAsEmbed = tc => {
|
|||||||
text0.applyDelta([{
|
text0.applyDelta([{
|
||||||
insert: new Y.Map([['key', 'val']])
|
insert: new Y.Map([['key', 'val']])
|
||||||
}])
|
}])
|
||||||
t.compare(/** @type {delta.InsertEmbedOp<any>} */ (text0.getContent().ops[0]).insert.toJSON(), { key: 'val' })
|
t.compare(/** @type {delta.InsertEmbedOp<any>} */ (text0.getDelta().ops[0]).insert.toJSON(), { key: 'val' })
|
||||||
let firedEvent = false
|
let firedEvent = false
|
||||||
text1.observe(event => {
|
text1.observe(event => {
|
||||||
const d = event.delta
|
const d = event.delta
|
||||||
@@ -1813,7 +1813,7 @@ export const testTypesAsEmbed = tc => {
|
|||||||
firedEvent = true
|
firedEvent = true
|
||||||
})
|
})
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
const delta = text1.getContent().toJSON()
|
const delta = text1.getDelta().toJSON()
|
||||||
t.assert(delta.length === 1)
|
t.assert(delta.length === 1)
|
||||||
t.compare(/** @type {any} */ (delta[0]).insert.toJSON(), { key: 'val' })
|
t.compare(/** @type {any} */ (delta[0]).insert.toJSON(), { key: 'val' })
|
||||||
t.assert(firedEvent, 'fired the event observer containing a Type-Embed')
|
t.assert(firedEvent, 'fired the event observer containing a Type-Embed')
|
||||||
@@ -1847,11 +1847,11 @@ export const testSnapshot = tc => {
|
|||||||
}, {
|
}, {
|
||||||
delete: 1
|
delete: 1
|
||||||
}])
|
}])
|
||||||
const state1 = text0.getContent(createAttributionManagerFromSnapshots(snapshot1))
|
const state1 = text0.getDelta(createAttributionManagerFromSnapshots(snapshot1))
|
||||||
t.compare(state1.toJSON(), [{ insert: 'abcd' }])
|
t.compare(state1.toJSON(), [{ insert: 'abcd' }])
|
||||||
const state2 = text0.getContent(createAttributionManagerFromSnapshots(snapshot2))
|
const state2 = text0.getDelta(createAttributionManagerFromSnapshots(snapshot2))
|
||||||
t.compare(state2.toJSON(), [{ insert: 'axcd' }])
|
t.compare(state2.toJSON(), [{ insert: 'axcd' }])
|
||||||
const state2Diff = text0.getContent(createAttributionManagerFromSnapshots(snapshot1, snapshot2)).toJSON()
|
const state2Diff = text0.getDelta(createAttributionManagerFromSnapshots(snapshot1, snapshot2)).toJSON()
|
||||||
const expected = [{ insert: 'a' }, { insert: 'x', attribution: { insert: [] } }, { insert: 'b', attribution: { delete: [] } }, { insert: 'cd' }]
|
const expected = [{ insert: 'a' }, { insert: 'x', attribution: { insert: [] } }, { insert: 'b', attribution: { delete: [] } }, { insert: 'cd' }]
|
||||||
t.compare(state2Diff, expected)
|
t.compare(state2Diff, expected)
|
||||||
}
|
}
|
||||||
@@ -1872,7 +1872,7 @@ export const testSnapshotDeleteAfter = tc => {
|
|||||||
}, {
|
}, {
|
||||||
insert: 'e'
|
insert: 'e'
|
||||||
}])
|
}])
|
||||||
const state1 = text0.getContent(createAttributionManagerFromSnapshots(snapshot1))
|
const state1 = text0.getDelta(createAttributionManagerFromSnapshots(snapshot1))
|
||||||
t.compare(state1, delta.fromJSON([{ insert: 'abcd' }]))
|
t.compare(state1, delta.fromJSON([{ insert: 'abcd' }]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1892,7 +1892,7 @@ export const testToDeltaEmbedAttributes = tc => {
|
|||||||
const { text0 } = init(tc, { users: 1 })
|
const { text0 } = init(tc, { users: 1 })
|
||||||
text0.insert(0, 'ab', { bold: true })
|
text0.insert(0, 'ab', { bold: true })
|
||||||
text0.insertEmbed(1, { image: 'imageSrc.png' }, { width: 100 })
|
text0.insertEmbed(1, { image: 'imageSrc.png' }, { width: 100 })
|
||||||
const delta0 = text0.getContent().toJSON()
|
const delta0 = text0.getDelta().toJSON()
|
||||||
t.compare(delta0, [{ insert: 'a', attributes: { bold: true } }, { insert: { image: 'imageSrc.png' }, attributes: { width: 100 } }, { insert: 'b', attributes: { bold: true } }])
|
t.compare(delta0, [{ insert: 'a', attributes: { bold: true } }, { insert: { image: 'imageSrc.png' }, attributes: { width: 100 } }, { insert: 'b', attributes: { bold: true } }])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1903,7 +1903,7 @@ export const testToDeltaEmbedNoAttributes = tc => {
|
|||||||
const { text0 } = init(tc, { users: 1 })
|
const { text0 } = init(tc, { users: 1 })
|
||||||
text0.insert(0, 'ab', { bold: true })
|
text0.insert(0, 'ab', { bold: true })
|
||||||
text0.insertEmbed(1, { image: 'imageSrc.png' })
|
text0.insertEmbed(1, { image: 'imageSrc.png' })
|
||||||
const delta0 = text0.getContent().toJSON()
|
const delta0 = text0.getDelta().toJSON()
|
||||||
t.compare(delta0, [{ insert: 'a', attributes: { bold: true } }, { insert: { image: 'imageSrc.png' } }, { insert: 'b', attributes: { bold: true } }], 'toDelta does not set attributes key when no attributes are present')
|
t.compare(delta0, [{ insert: 'a', attributes: { bold: true } }, { insert: { image: 'imageSrc.png' } }, { insert: 'b', attributes: { bold: true } }], 'toDelta does not set attributes key when no attributes are present')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2211,9 +2211,9 @@ export const testFormattingBug = async _tc => {
|
|||||||
{ insert: '\n', attributes: { url: 'http://docs.yjs.dev' } },
|
{ insert: '\n', attributes: { url: 'http://docs.yjs.dev' } },
|
||||||
{ insert: '\n', attributes: { url: 'http://example.com' } }
|
{ insert: '\n', attributes: { url: 'http://example.com' } }
|
||||||
]
|
]
|
||||||
t.compare(text1.getContent().toJSON(), expectedResult)
|
t.compare(text1.getDelta().toJSON(), expectedResult)
|
||||||
t.compare(text1.getContent().toJSON(), text2.getContent().toJSON())
|
t.compare(text1.getDelta().toJSON(), text2.getDelta().toJSON())
|
||||||
console.log(text1.getContent().toJSON())
|
console.log(text1.getDelta().toJSON())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2241,8 +2241,8 @@ export const testDeleteFormatting = _tc => {
|
|||||||
{ insert: 'on ', attributes: { bold: true } },
|
{ insert: 'on ', attributes: { bold: true } },
|
||||||
{ insert: 'fire off the shoulder of Orion.' }
|
{ insert: 'fire off the shoulder of Orion.' }
|
||||||
]
|
]
|
||||||
t.compare(text.getContent().toJSON(), expected)
|
t.compare(text.getDelta().toJSON(), expected)
|
||||||
t.compare(text2.getContent().toJSON(), expected)
|
t.compare(text2.getDelta().toJSON(), expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2261,14 +2261,14 @@ 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' }])
|
||||||
const expectedContent = delta.createTextDelta().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('!')
|
||||||
const attributedContent = ytext.getContent(attributionManager)
|
const attributedContent = ytext.getDelta(attributionManager)
|
||||||
console.log(attributedContent.toJSON())
|
console.log(attributedContent.toJSON())
|
||||||
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 } }])
|
||||||
const expectedContent = delta.createTextDelta().insert('Hell', null, { attributes: { italic: [] } }).insert('o attributions!')
|
const expectedContent = delta.createTextDelta().insert('Hell', null, { attributes: { italic: [] } }).insert('o attributions!')
|
||||||
const attributedContent = ytext.getContent(attributionManager)
|
const attributedContent = ytext.getDelta(attributionManager)
|
||||||
console.log(attributedContent.toJSON())
|
console.log(attributedContent.toJSON())
|
||||||
t.assert(attributedContent.equals(expectedContent))
|
t.assert(attributedContent.equals(expectedContent))
|
||||||
})
|
})
|
||||||
@@ -2299,7 +2299,7 @@ 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
|
||||||
const attributedContent = ytext.getContent(attributionManager)
|
const attributedContent = ytext.getDelta(attributionManager)
|
||||||
console.log(JSON.stringify(attributedContent.toJSON(), null, 2))
|
console.log(JSON.stringify(attributedContent.toJSON(), null, 2))
|
||||||
const expectedContent = delta.createTextDelta().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))
|
||||||
@@ -2583,7 +2583,7 @@ export const testAttributionManagerDefaultPerformance = tc => {
|
|||||||
})
|
})
|
||||||
t.measureTime(`getContent(attributionManager) performance <executed ${M} times>`, () => {
|
t.measureTime(`getContent(attributionManager) performance <executed ${M} times>`, () => {
|
||||||
for (let i = 0; i < M; i++) {
|
for (let i = 0; i < M; i++) {
|
||||||
ytext.getContent()
|
ytext.getDelta()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -207,7 +207,7 @@ export const testFormattingBug = _tc => {
|
|||||||
{ insert: 'C', attributes: { em: {}, strong: {} } }
|
{ insert: 'C', attributes: { em: {}, strong: {} } }
|
||||||
]
|
]
|
||||||
yxml.applyDelta(delta)
|
yxml.applyDelta(delta)
|
||||||
t.compare(yxml.getContent().toJSON(), delta)
|
t.compare(yxml.getDelta().toJSON(), delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -244,10 +244,10 @@ export const testFragmentAttributedContent = _tc => {
|
|||||||
yfragment.insert(1, [elem3])
|
yfragment.insert(1, [elem3])
|
||||||
})
|
})
|
||||||
const 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: [] })
|
||||||
const attributedContent = yfragment.getContent(attributionManager)
|
const attributedContent = yfragment.getDelta(attributionManager)
|
||||||
console.log(attributedContent.children.toJSON())
|
console.log(attributedContent.children.toJSON())
|
||||||
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.getDelta(attributionManager).toJSON(), delta.createTextDelta().insert('hello', null, { delete: [] }).done().toJSON())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +273,7 @@ export const testElementAttributedContent = _tc => {
|
|||||||
yelement.setAttribute('key', '42')
|
yelement.setAttribute('key', '42')
|
||||||
})
|
})
|
||||||
const 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: [] })
|
||||||
const attributedContent = yelement.getContent(attributionManager)
|
const attributedContent = yelement.getDelta(attributionManager)
|
||||||
console.log('children', attributedContent.children.toJSON())
|
console.log('children', attributedContent.children.toJSON())
|
||||||
console.log('attributes', attributedContent.attributes)
|
console.log('attributes', attributedContent.attributes)
|
||||||
t.assert(attributedContent.children.equals(expectedContent))
|
t.assert(attributedContent.children.equals(expectedContent))
|
||||||
|
|||||||
Reference in New Issue
Block a user