mirror of
https://github.com/yjs/yjs.git
synced 2026-02-24 04:01:14 +01:00
implement and test getContent on all types
This commit is contained in:
@@ -109,7 +109,10 @@ export {
|
||||
diffIdMap,
|
||||
diffIdSet,
|
||||
AttributionItem as Attribution,
|
||||
encodeIdMap
|
||||
encodeIdMap,
|
||||
createIdMapFromIdSet,
|
||||
TwosetAttributionManager,
|
||||
noAttributionsManager
|
||||
} from './internals.js'
|
||||
|
||||
const glo = /** @type {any} */ (typeof globalThis !== 'undefined'
|
||||
|
||||
@@ -10,9 +10,12 @@ import {
|
||||
ContentAny,
|
||||
ContentBinary,
|
||||
getItemCleanStart,
|
||||
ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item, // eslint-disable-line
|
||||
ContentDoc, YText, YArray, UpdateEncoderV1, UpdateEncoderV2, Doc, Snapshot, Transaction, EventHandler, YEvent, Item,
|
||||
createAttributionFromAttrs, // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
import * as delta from '../utils/Delta.js'
|
||||
import * as array from 'lib0/array'
|
||||
import * as map from 'lib0/map'
|
||||
import * as iterator from 'lib0/iterator'
|
||||
import * as error from 'lib0/error'
|
||||
@@ -466,6 +469,42 @@ export const typeListToArray = type => {
|
||||
return cs
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, .. }`.
|
||||
*
|
||||
* @template MapType
|
||||
* @param {AbstractType<any>} type
|
||||
* @param {import('../internals.js').AbstractAttributionManager} am
|
||||
* @return {delta.Delta<Array<MapType>>} The Delta representation of this type.
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
export const typeListGetContent = (type, am) => {
|
||||
type.doc ?? warnPrematureAccess()
|
||||
const d = /** @type {delta.DeltaBuilder<Array<MapType>>} */ (delta.create())
|
||||
/**
|
||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||
*/
|
||||
const cs = []
|
||||
for (let item = type._start; item !== null; cs.length = 0) {
|
||||
// populate cs
|
||||
for (; item !== null && cs.length < 50; item = item.right) {
|
||||
am.readContent(cs, item)
|
||||
}
|
||||
for (let i = 0; i < cs.length; i++) {
|
||||
const { content, deleted, attrs } = cs[i]
|
||||
const attribution = createAttributionFromAttrs(attrs, deleted)
|
||||
d.insert(content.getContent(), null, attribution)
|
||||
}
|
||||
}
|
||||
return d.done()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {AbstractType<any>} type
|
||||
* @param {Snapshot} snapshot
|
||||
@@ -913,6 +952,71 @@ export const typeMapGetAll = (parent) => {
|
||||
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
|
||||
* attributions.
|
||||
*
|
||||
* Note that deleted content that was not deleted in prevYdoc is rendered as an insertion with the
|
||||
* attribution `{ isDeleted: true, .. }`.
|
||||
*
|
||||
* @template MapType
|
||||
* @param {AbstractType<any>} parent
|
||||
* @param {import('../internals.js').AbstractAttributionManager} am
|
||||
* @return {MapAttributedContent<MapType>} The Delta representation of this type.
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
export const typeMapGetContent = (parent, am) => {
|
||||
/**
|
||||
* @type {MapAttributedContent<MapType>}
|
||||
*/
|
||||
const mapcontent = {}
|
||||
parent.doc ?? warnPrematureAccess()
|
||||
parent._map.forEach((item, key) => {
|
||||
/**
|
||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||
*/
|
||||
const cs = []
|
||||
am.readContent(cs, item)
|
||||
const { deleted, attrs, content } = cs[cs.length - 1]
|
||||
const c = array.last(content.getContent())
|
||||
const attribution = createAttributionFromAttrs(attrs, deleted)
|
||||
if (deleted) {
|
||||
mapcontent[key] = { prevValue: c, value: undefined, attribution }
|
||||
} else {
|
||||
/**
|
||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||
*/
|
||||
let cs = []
|
||||
for (let prevItem = item.left; prevItem != null; prevItem = prevItem.left) {
|
||||
/**
|
||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||
*/
|
||||
const tmpcs = []
|
||||
am.readContent(tmpcs, prevItem)
|
||||
cs = tmpcs.concat(cs)
|
||||
if (cs[0].attrs == null) {
|
||||
cs.splice(0, cs.findIndex(c => c.attrs != null))
|
||||
break
|
||||
}
|
||||
if (cs.length > 0) {
|
||||
cs.length = 1
|
||||
}
|
||||
}
|
||||
const prevValue = cs.length > 0 ? array.last(cs[0].content.getContent()) : undefined
|
||||
mapcontent[key] = { prevValue, value: c, attribution }
|
||||
}
|
||||
})
|
||||
return mapcontent
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {AbstractType<any>} parent
|
||||
* @param {string} key
|
||||
|
||||
@@ -17,9 +17,10 @@ import {
|
||||
callTypeObservers,
|
||||
transact,
|
||||
warnPrematureAccess,
|
||||
ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item // eslint-disable-line
|
||||
ArraySearchMarker, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item, // eslint-disable-line
|
||||
AbstractAttributionManager
|
||||
} from '../internals.js'
|
||||
import { typeListSlice } from './AbstractType.js'
|
||||
import { typeListGetContent, typeListSlice } from './AbstractType.js'
|
||||
|
||||
/**
|
||||
* Event that describes the changes on a YArray
|
||||
@@ -207,6 +208,22 @@ export class YArray extends AbstractType {
|
||||
return typeListToArray(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 {import('../utils/Delta.js').Delta<Array<T>>} The Delta representation of this type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getContent (am) {
|
||||
return typeListGetContent(this, am)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a portion of this YArray into a JavaScript Array selected
|
||||
* from start to end (end not included).
|
||||
|
||||
@@ -15,17 +15,13 @@ import {
|
||||
transact,
|
||||
warnPrematureAccess,
|
||||
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Transaction, Item, // eslint-disable-line
|
||||
createAttributionFromAttrs
|
||||
createAttributionFromAttrs,
|
||||
typeMapGetContent
|
||||
} from '../internals.js'
|
||||
|
||||
import * as array from 'lib0/array'
|
||||
import * as iterator from 'lib0/iterator'
|
||||
|
||||
/**
|
||||
* @template MapType
|
||||
* @typedef {{ [key: string]: { prevValue: MapType | undefined, value: MapType | undefined, attribution: any } }} MapAttributedContent
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @extends YEvent<YMap<T>>
|
||||
@@ -201,51 +197,12 @@ export class YMap extends AbstractType {
|
||||
* attribution `{ isDeleted: true, .. }`.
|
||||
*
|
||||
* @param {import('../internals.js').AbstractAttributionManager} am
|
||||
* @return {MapAttributedContent<MapType>} The Delta representation of this type.
|
||||
* @return {import('./AbstractType.js').MapAttributedContent<MapType>} The Delta representation of this type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getContent (am) {
|
||||
/**
|
||||
* @type {MapAttributedContent<MapType>}
|
||||
*/
|
||||
const mapcontent = {}
|
||||
this._map.forEach((item, key) => {
|
||||
/**
|
||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||
*/
|
||||
const cs = []
|
||||
am.readContent(cs, item)
|
||||
const { deleted, attrs, content } = cs[cs.length - 1]
|
||||
const c = array.last(content.getContent())
|
||||
const attribution = createAttributionFromAttrs(attrs, deleted)
|
||||
if (deleted) {
|
||||
mapcontent[key] = { prevValue: c, value: undefined, attribution }
|
||||
} else {
|
||||
/**
|
||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||
*/
|
||||
let cs = []
|
||||
for (let prevItem = item.left; prevItem != null; prevItem = prevItem.left) {
|
||||
/**
|
||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||
*/
|
||||
const tmpcs = []
|
||||
am.readContent(tmpcs, prevItem)
|
||||
cs = tmpcs.concat(cs)
|
||||
if (cs[0].attrs == null) {
|
||||
cs.splice(0, cs.findIndex(c => c.attrs != null))
|
||||
break
|
||||
}
|
||||
if (cs.length > 0) {
|
||||
cs.length = 1
|
||||
}
|
||||
}
|
||||
const prevValue = cs.length > 0 ? array.last(cs[0].content.getContent()) : undefined
|
||||
mapcontent[key] = { prevValue, value: c, attribution }
|
||||
}
|
||||
})
|
||||
return mapcontent
|
||||
return typeMapGetContent(this, am)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1000,13 +1000,13 @@ export class YText extends AbstractType {
|
||||
* attribution `{ isDeleted: true, .. }`.
|
||||
*
|
||||
* @param {AbstractAttributionManager} am
|
||||
* @return {import('../utils/Delta.js').Delta} The Delta representation of this type.
|
||||
* @return {import('../utils/Delta.js').Delta<import('../utils/Delta.js').TextDeltaContent>} The Delta representation of this type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getContent (am = noAttributionsManager) {
|
||||
this.doc ?? warnPrematureAccess()
|
||||
const d = delta.create()
|
||||
const d = delta.createTextDelta()
|
||||
/**
|
||||
* @type {Array<import('../internals.js').AttributedContent<any>>}
|
||||
*/
|
||||
|
||||
@@ -11,7 +11,9 @@ import {
|
||||
typeMapGetAllSnapshot,
|
||||
typeListForEach,
|
||||
YXmlElementRefID,
|
||||
Snapshot, YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item // eslint-disable-line
|
||||
typeMapGetContent,
|
||||
noAttributionsManager,
|
||||
Snapshot, YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item, // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
/**
|
||||
@@ -206,6 +208,23 @@ export class YXmlElement extends YXmlFragment {
|
||||
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 {import('../internals.js').AbstractAttributionManager} am
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getContent (am = noAttributionsManager) {
|
||||
const attributes = typeMapGetContent(this, am)
|
||||
const { children } = super.getContent(am)
|
||||
return { children, attributes }
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Dom Element that mirrors this YXmlElement.
|
||||
*
|
||||
|
||||
@@ -18,7 +18,9 @@ import {
|
||||
typeListGet,
|
||||
typeListSlice,
|
||||
warnPrematureAccess,
|
||||
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, ContentType, Transaction, Item, YXmlText, YXmlHook // eslint-disable-line
|
||||
noAttributionsManager,
|
||||
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, ContentType, Transaction, Item, YXmlText, YXmlHook, // eslint-disable-line
|
||||
typeListGetContent
|
||||
} from '../internals.js'
|
||||
|
||||
import * as error from 'lib0/error'
|
||||
@@ -377,6 +379,17 @@ export class YXmlFragment extends AbstractType {
|
||||
return typeListToArray(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the attributed content using the attribution manager.
|
||||
*
|
||||
* @param {import('../internals.js').AbstractAttributionManager} am
|
||||
* @return {{ children: import('../utils/Delta.js').Delta<Array<YXmlElement|YXmlText|YXmlHook>> }}
|
||||
*/
|
||||
getContent (am = noAttributionsManager) {
|
||||
const children = typeListGetContent(this, am)
|
||||
return { children }
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends content to this YArray.
|
||||
*
|
||||
|
||||
@@ -2,7 +2,8 @@ import * as object from 'lib0/object'
|
||||
import * as fun from 'lib0/function'
|
||||
|
||||
/**
|
||||
* @typedef {InsertOp|RetainOp|DeleteOp} DeltaOp
|
||||
* @template {string|Array<any>|{[key: string]: any}} Content
|
||||
* @typedef {InsertOp<Content>|RetainOp|DeleteOp} DeltaOp
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -13,9 +14,12 @@ import * as fun from 'lib0/function'
|
||||
* @typedef {{ [key: string]: any }} FormattingAttributes
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {string|Array<any>|{[key: string]: any}} Content
|
||||
*/
|
||||
export class InsertOp {
|
||||
/**
|
||||
* @param {string} insert
|
||||
* @param {Content} insert
|
||||
* @param {FormattingAttributes|null} attributes
|
||||
* @param {Attribution|null} attribution
|
||||
*/
|
||||
@@ -60,16 +64,19 @@ export class RetainOp {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {string|Array<any>|{[key: string]: any}} Content
|
||||
*/
|
||||
export class Delta {
|
||||
constructor () {
|
||||
/**
|
||||
* @type {Array<DeltaOp>}
|
||||
* @type {Array<DeltaOp<Content>>}
|
||||
*/
|
||||
this.ops = []
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Delta} d
|
||||
* @param {Delta<Content>} d
|
||||
* @return {boolean}
|
||||
*/
|
||||
equals (d) {
|
||||
@@ -85,9 +92,9 @@ export class Delta {
|
||||
}
|
||||
case InsertOp: {
|
||||
if (
|
||||
!fun.equalityDeep(/** @type {InsertOp} */ (op).insert, /** @type {InsertOp} */ (dop).insert)
|
||||
|| !fun.equalityDeep(/** @type {InsertOp} */ (op).attributes, /** @type {InsertOp} */ (dop).attributes)
|
||||
|| !fun.equalityDeep(/** @type {InsertOp} */ (op).attribution, /** @type {InsertOp} */ (dop).attribution)
|
||||
!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
|
||||
}
|
||||
@@ -126,6 +133,10 @@ const mergeAttrs = (a, b) => {
|
||||
return merged
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {string|Array<any>|{[key: string]: any}} Content
|
||||
* @extends Delta<Content>
|
||||
*/
|
||||
export class DeltaBuilder extends Delta {
|
||||
constructor () {
|
||||
super()
|
||||
@@ -139,7 +150,7 @@ export class DeltaBuilder extends Delta {
|
||||
this.usedAttribution = null
|
||||
/**
|
||||
* @private
|
||||
* @type {DeltaOp?}
|
||||
* @type {DeltaOp<Content>?}
|
||||
*/
|
||||
this._lastOp = null
|
||||
}
|
||||
@@ -164,7 +175,7 @@ export class DeltaBuilder extends Delta {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} insert
|
||||
* @param {Content} insert
|
||||
* @param {FormattingAttributes?} attributes
|
||||
* @param {Attribution?} attribution
|
||||
* @return {this}
|
||||
@@ -173,7 +184,15 @@ export class DeltaBuilder extends Delta {
|
||||
const mergedAttributes = attributes == null ? this.usedAttributes : mergeAttrs(this.usedAttributes, attributes)
|
||||
const mergedAttribution = attribution == null ? this.usedAttribution : mergeAttrs(this.usedAttribution, attribution)
|
||||
if (this._lastOp instanceof InsertOp && (mergedAttributes === this._lastOp.attributes || fun.equalityDeep(mergedAttributes, this._lastOp.attributes)) && (mergedAttribution === this._lastOp.attribution || fun.equalityDeep(mergedAttribution, this._lastOp.attribution))) {
|
||||
this._lastOp.insert += insert
|
||||
if (insert.constructor === String) {
|
||||
// @ts-ignore
|
||||
this._lastOp.insert += insert
|
||||
} else if (insert.constructor === Array && this._lastOp.insert.constructor === Array) {
|
||||
// @ts-ignore
|
||||
this._lastOp.insert.push(...insert)
|
||||
} else {
|
||||
this.ops.push(this._lastOp = new InsertOp(insert, mergedAttributes, mergedAttribution))
|
||||
}
|
||||
} else {
|
||||
this.ops.push(this._lastOp = new InsertOp(insert, mergedAttributes, mergedAttribution))
|
||||
}
|
||||
@@ -211,7 +230,7 @@ export class DeltaBuilder extends Delta {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Delta}
|
||||
* @return {Delta<Content>}
|
||||
*/
|
||||
done () {
|
||||
return this
|
||||
@@ -219,3 +238,23 @@ export class DeltaBuilder extends Delta {
|
||||
}
|
||||
|
||||
export const create = () => new DeltaBuilder()
|
||||
|
||||
/**
|
||||
* @typedef {string | { [key: string]: any }} TextDeltaContent
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {TextDeltaContent} Content
|
||||
* @return {DeltaBuilder<Content>}
|
||||
*/
|
||||
export const createTextDelta = () => new DeltaBuilder()
|
||||
|
||||
/**
|
||||
* @typedef {Array<any>} ArrayDeltaContent
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template {ArrayDeltaContent} Content
|
||||
* @return {DeltaBuilder<Content>}
|
||||
*/
|
||||
export const createArrayDelta = () => new DeltaBuilder()
|
||||
|
||||
@@ -5,6 +5,21 @@ import * as delta from '../src/utils/Delta.js'
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testDelta = _tc => {
|
||||
const d = delta.create().insert('hello').insert(' ').useAttributes({ bold: true }).insert('world').useAttribution({ creator: 'tester' }).insert('!').done()
|
||||
t.compare(d.toJSON().ops, [{ insert: 'hello ' }, { insert: 'world', attributes: { bold: true } }, { insert: '!', attributes: { bold: true }, attribution: { creator: 'tester' } }])
|
||||
const d = delta.create().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'] } }])
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testDeltaMerging = _tc => {
|
||||
const d = delta.create()
|
||||
.insert('hello')
|
||||
.insert('world')
|
||||
.insert(' ', { italic: true })
|
||||
.insert({})
|
||||
.insert([1])
|
||||
.insert([2])
|
||||
.done()
|
||||
t.compare(d.toJSON().ops, [{ insert: 'helloworld' }, { insert: ' ', attributes: { italic: true } }, { insert: {} }, { insert: [1, 2] }])
|
||||
}
|
||||
|
||||
@@ -4,13 +4,14 @@ import * as t from 'lib0/testing'
|
||||
import * as prng from 'lib0/prng'
|
||||
import * as math from 'lib0/math'
|
||||
import * as env from 'lib0/environment'
|
||||
import * as delta from '../src/utils/Delta.js'
|
||||
|
||||
const isDevMode = env.getVariable('node_env') === 'development'
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testBasicUpdate = tc => {
|
||||
export const testBasicUpdate = _tc => {
|
||||
const doc1 = new Y.Doc()
|
||||
const doc2 = new Y.Doc()
|
||||
doc1.getArray('array').insert(0, ['hi'])
|
||||
@@ -20,9 +21,9 @@ export const testBasicUpdate = tc => {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testFailsObjectManipulationInDevMode = tc => {
|
||||
export const testFailsObjectManipulationInDevMode = _tc => {
|
||||
if (isDevMode) {
|
||||
t.info('running in dev mode')
|
||||
const doc = new Y.Doc()
|
||||
@@ -42,9 +43,9 @@ export const testFailsObjectManipulationInDevMode = tc => {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testSlice = tc => {
|
||||
export const testSlice = _tc => {
|
||||
const doc1 = new Y.Doc()
|
||||
const arr = doc1.getArray('array')
|
||||
arr.insert(0, [1, 2, 3])
|
||||
@@ -57,9 +58,9 @@ export const testSlice = tc => {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testArrayFrom = tc => {
|
||||
export const testArrayFrom = _tc => {
|
||||
const doc1 = new Y.Doc()
|
||||
const db1 = doc1.getMap('root')
|
||||
const nestedArray1 = Y.Array.from([0, 1, 2])
|
||||
@@ -70,9 +71,9 @@ export const testArrayFrom = tc => {
|
||||
/**
|
||||
* Debugging yjs#297 - a critical bug connected to the search-marker approach
|
||||
*
|
||||
* @param {t.TestCase} tc
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testLengthIssue = tc => {
|
||||
export const testLengthIssue = _tc => {
|
||||
const doc1 = new Y.Doc()
|
||||
const arr = doc1.getArray('array')
|
||||
arr.push([0, 1, 2, 3])
|
||||
@@ -99,9 +100,9 @@ export const testLengthIssue = tc => {
|
||||
/**
|
||||
* Debugging yjs#314
|
||||
*
|
||||
* @param {t.TestCase} tc
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testLengthIssue2 = tc => {
|
||||
export const testLengthIssue2 = _tc => {
|
||||
const doc = new Y.Doc()
|
||||
const next = doc.getArray()
|
||||
doc.transact(() => {
|
||||
@@ -288,7 +289,7 @@ export const testNestedObserverEvents = tc => {
|
||||
* @type {Array<number>}
|
||||
*/
|
||||
const vals = []
|
||||
array0.observe(e => {
|
||||
array0.observe(() => {
|
||||
if (array0.length === 1) {
|
||||
// inserting, will call this observer again
|
||||
// we expect that this observer is called after this event handler finishedn
|
||||
@@ -491,9 +492,9 @@ export const testEventTargetIsSetCorrectlyOnRemote = tc => {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testIteratingArrayContainingTypes = tc => {
|
||||
export const testIteratingArrayContainingTypes = _tc => {
|
||||
const y = new Y.Doc()
|
||||
const arr = y.getArray('arr')
|
||||
const numItems = 10
|
||||
@@ -509,6 +510,31 @@ export const testIteratingArrayContainingTypes = tc => {
|
||||
y.destroy()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testAttributedContent = _tc => {
|
||||
const ydoc = new Y.Doc({ gc: false })
|
||||
const yarray = ydoc.getArray()
|
||||
yarray.insert(0, [1, 2])
|
||||
let attributionManager = Y.noAttributionsManager
|
||||
|
||||
ydoc.on('afterTransaction', tr => {
|
||||
// attributionManager = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, [new Y.Attribution('insertedAt', 42), new Y.Attribution('insert', 'kevin')]), createIdMapFromIdSet(tr.deleteSet, [new Y.Attribution('delete', 'kevin')]))
|
||||
attributionManager = new Y.TwosetAttributionManager(Y.createIdMapFromIdSet(tr.insertSet, []), Y.createIdMapFromIdSet(tr.deleteSet, []))
|
||||
})
|
||||
t.group('insert / delete', () => {
|
||||
ydoc.transact(() => {
|
||||
yarray.delete(0, 1)
|
||||
yarray.insert(1, [42])
|
||||
})
|
||||
let expectedContent = delta.createArrayDelta().insert([1], null, { delete: [] }).insert([2]).insert([42], null, { insert: [] })
|
||||
let attributedContent = yarray.getContent(attributionManager)
|
||||
console.log(attributedContent.toJSON().ops)
|
||||
t.assert(attributedContent.equals(expectedContent))
|
||||
})
|
||||
}
|
||||
|
||||
let _uniqueNumber = 0
|
||||
const getUniqueNumber = () => _uniqueNumber++
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as Y from '../src/index.js'
|
||||
import { init, compare } from './testHelper.js'
|
||||
import * as t from 'lib0/testing'
|
||||
import * as delta from '../src/utils/Delta.js'
|
||||
|
||||
export const testCustomTypings = () => {
|
||||
const ydoc = new Y.Doc()
|
||||
@@ -220,3 +221,63 @@ export const testElement = _tc => {
|
||||
yxmlel.insert(0, [text1, text2])
|
||||
t.compareArrays(yxmlel.toArray(), [text1, text2])
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testFragmentAttributedContent = _tc => {
|
||||
const ydoc = new Y.Doc({ gc: false })
|
||||
const yfragment = new Y.XmlFragment()
|
||||
const elem1 = new Y.XmlText('hello')
|
||||
const elem2 = new Y.XmlElement()
|
||||
const elem3 = new Y.XmlText('world')
|
||||
yfragment.insert(0, [elem1, elem2])
|
||||
ydoc.getArray().insert(0, [yfragment])
|
||||
let attributionManager = Y.noAttributionsManager
|
||||
ydoc.on('afterTransaction', tr => {
|
||||
// attributionManager = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, [new Y.Attribution('insertedAt', 42), new Y.Attribution('insert', 'kevin')]), createIdMapFromIdSet(tr.deleteSet, [new Y.Attribution('delete', 'kevin')]))
|
||||
attributionManager = new Y.TwosetAttributionManager(Y.createIdMapFromIdSet(tr.insertSet, []), Y.createIdMapFromIdSet(tr.deleteSet, []))
|
||||
})
|
||||
t.group('insert / delete', () => {
|
||||
ydoc.transact(() => {
|
||||
yfragment.delete(0, 1)
|
||||
yfragment.insert(1, [elem3])
|
||||
})
|
||||
let expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] })
|
||||
let attributedContent = yfragment.getContent(attributionManager)
|
||||
console.log(attributedContent.children.toJSON().ops)
|
||||
t.assert(attributedContent.children.equals(expectedContent))
|
||||
t.compare(elem1.getContent(attributionManager).toJSON(), delta.createTextDelta().insert('hello', null, { delete: [] }).done().toJSON())
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testElementAttributedContent = _tc => {
|
||||
const ydoc = new Y.Doc({ gc: false })
|
||||
const yelement = ydoc.getXmlElement('p')
|
||||
const elem1 = new Y.XmlElement('span')
|
||||
const elem2 = new Y.XmlText('hello')
|
||||
const elem3 = new Y.XmlText('world')
|
||||
yelement.insert(0, [elem1, elem2])
|
||||
let attributionManager = Y.noAttributionsManager
|
||||
ydoc.on('afterTransaction', tr => {
|
||||
// attributionManager = new TwosetAttributionManager(createIdMapFromIdSet(tr.insertSet, [new Y.Attribution('insertedAt', 42), new Y.Attribution('insert', 'kevin')]), createIdMapFromIdSet(tr.deleteSet, [new Y.Attribution('delete', 'kevin')]))
|
||||
attributionManager = new Y.TwosetAttributionManager(Y.createIdMapFromIdSet(tr.insertSet, []), Y.createIdMapFromIdSet(tr.deleteSet, []))
|
||||
})
|
||||
t.group('insert / delete', () => {
|
||||
ydoc.transact(() => {
|
||||
yelement.delete(0, 1)
|
||||
yelement.insert(1, [elem3])
|
||||
yelement.setAttribute('key', '42')
|
||||
})
|
||||
let expectedContent = delta.createArrayDelta().insert([elem1], null, { delete: [] }).insert([elem2]).insert([elem3], null, { insert: [] })
|
||||
let attributedContent = yelement.getContent(attributionManager)
|
||||
console.log('children', attributedContent.children.toJSON().ops)
|
||||
console.log('attributes', attributedContent.attributes)
|
||||
t.assert(attributedContent.children.equals(expectedContent))
|
||||
t.compare(attributedContent.attributes, { key: { prevValue: undefined, value: '42', attribution: { insert: [] } } })
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user