quotations ove text with XML formatted strings
This commit is contained in:
parent
8788a4b9e0
commit
160c9ca1b7
@ -1002,107 +1002,7 @@ export class YText extends AbstractType {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
toDelta (snapshot, prevSnapshot, computeYChange) {
|
toDelta (snapshot, prevSnapshot, computeYChange) {
|
||||||
/**
|
return rangeDelta(this, null, null, snapshot, prevSnapshot, computeYChange)
|
||||||
* @type{Array<any>}
|
|
||||||
*/
|
|
||||||
const ops = []
|
|
||||||
const currentAttributes = new Map()
|
|
||||||
const doc = /** @type {Doc} */ (this.doc)
|
|
||||||
let str = ''
|
|
||||||
let n = this._start
|
|
||||||
function packStr () {
|
|
||||||
if (str.length > 0) {
|
|
||||||
// pack str with attributes to ops
|
|
||||||
/**
|
|
||||||
* @type {Object<string,any>}
|
|
||||||
*/
|
|
||||||
const attributes = {}
|
|
||||||
let addAttributes = false
|
|
||||||
currentAttributes.forEach((value, key) => {
|
|
||||||
addAttributes = true
|
|
||||||
attributes[key] = value
|
|
||||||
})
|
|
||||||
/**
|
|
||||||
* @type {Object<string,any>}
|
|
||||||
*/
|
|
||||||
const op = { insert: str }
|
|
||||||
if (addAttributes) {
|
|
||||||
op.attributes = attributes
|
|
||||||
}
|
|
||||||
ops.push(op)
|
|
||||||
str = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const computeDelta = () => {
|
|
||||||
while (n !== null) {
|
|
||||||
if (isVisible(n, snapshot) || (prevSnapshot !== undefined && isVisible(n, prevSnapshot))) {
|
|
||||||
switch (n.content.constructor) {
|
|
||||||
case ContentString: {
|
|
||||||
const cur = currentAttributes.get('ychange')
|
|
||||||
if (snapshot !== undefined && !isVisible(n, snapshot)) {
|
|
||||||
if (cur === undefined || cur.user !== n.id.client || cur.type !== 'removed') {
|
|
||||||
packStr()
|
|
||||||
currentAttributes.set('ychange', computeYChange ? computeYChange('removed', n.id) : { type: 'removed' })
|
|
||||||
}
|
|
||||||
} else if (prevSnapshot !== undefined && !isVisible(n, prevSnapshot)) {
|
|
||||||
if (cur === undefined || cur.user !== n.id.client || cur.type !== 'added') {
|
|
||||||
packStr()
|
|
||||||
currentAttributes.set('ychange', computeYChange ? computeYChange('added', n.id) : { type: 'added' })
|
|
||||||
}
|
|
||||||
} else if (cur !== undefined) {
|
|
||||||
packStr()
|
|
||||||
currentAttributes.delete('ychange')
|
|
||||||
}
|
|
||||||
str += /** @type {ContentString} */ (n.content).str
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case ContentType:
|
|
||||||
case ContentEmbed: {
|
|
||||||
packStr()
|
|
||||||
/**
|
|
||||||
* @type {Object<string,any>}
|
|
||||||
*/
|
|
||||||
const op = {
|
|
||||||
insert: n.content.getContent()[0]
|
|
||||||
}
|
|
||||||
if (currentAttributes.size > 0) {
|
|
||||||
const attrs = /** @type {Object<string,any>} */ ({})
|
|
||||||
op.attributes = attrs
|
|
||||||
currentAttributes.forEach((value, key) => {
|
|
||||||
attrs[key] = value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ops.push(op)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case ContentFormat:
|
|
||||||
if (isVisible(n, snapshot)) {
|
|
||||||
packStr()
|
|
||||||
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (n.content))
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
n = n.right
|
|
||||||
}
|
|
||||||
packStr()
|
|
||||||
}
|
|
||||||
if (snapshot || prevSnapshot) {
|
|
||||||
// snapshots are merged again after the transaction, so we need to keep the
|
|
||||||
// transaction alive until we are done
|
|
||||||
transact(doc, transaction => {
|
|
||||||
if (snapshot) {
|
|
||||||
splitSnapshotAffectedStructs(transaction, snapshot)
|
|
||||||
}
|
|
||||||
if (prevSnapshot) {
|
|
||||||
splitSnapshotAffectedStructs(transaction, prevSnapshot)
|
|
||||||
}
|
|
||||||
computeDelta()
|
|
||||||
}, 'cleanup')
|
|
||||||
} else {
|
|
||||||
computeDelta()
|
|
||||||
}
|
|
||||||
return ops
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1179,7 +1079,7 @@ export class YText extends AbstractType {
|
|||||||
return quoteText(transaction, this, pos, length)
|
return quoteText(transaction, this, pos, length)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
throw new Error('cannot quote YText which has not been integrated into any Doc')
|
throw new Error('Quoted text was not integrated into Doc')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1315,3 +1215,138 @@ export class YText extends AbstractType {
|
|||||||
* @function
|
* @function
|
||||||
*/
|
*/
|
||||||
export const readYText = _decoder => new YText()
|
export const readYText = _decoder => new YText()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a delta representation that happens between `start` and `end` ranges (both sides inclusive).
|
||||||
|
*
|
||||||
|
* @param {AbstractType<any>} parent
|
||||||
|
* @param {ID|null} start
|
||||||
|
* @param {ID|null} end
|
||||||
|
* @param {Snapshot|undefined} snapshot
|
||||||
|
* @param {Snapshot|undefined} prevSnapshot
|
||||||
|
* @param {(function('removed' | 'added', ID):any)|undefined} computeYChange
|
||||||
|
* @returns {any} The Delta representation of this type.
|
||||||
|
*/
|
||||||
|
export const rangeDelta = (parent, start, end, snapshot, prevSnapshot, computeYChange) => {
|
||||||
|
/**
|
||||||
|
* @type{Array<any>}
|
||||||
|
*/
|
||||||
|
const ops = []
|
||||||
|
const currentAttributes = new Map()
|
||||||
|
const doc = /** @type {Doc} */ (parent.doc)
|
||||||
|
let str = ''
|
||||||
|
let n = parent._start
|
||||||
|
function packStr () {
|
||||||
|
if (str.length > 0) {
|
||||||
|
// pack str with attributes to ops
|
||||||
|
/**
|
||||||
|
* @type {Object<string,any>}
|
||||||
|
*/
|
||||||
|
const attributes = {}
|
||||||
|
let addAttributes = false
|
||||||
|
currentAttributes.forEach((value, key) => {
|
||||||
|
addAttributes = true
|
||||||
|
attributes[key] = value
|
||||||
|
})
|
||||||
|
/**
|
||||||
|
* @type {Object<string,any>}
|
||||||
|
*/
|
||||||
|
const op = { insert: str }
|
||||||
|
if (addAttributes) {
|
||||||
|
op.attributes = attributes
|
||||||
|
}
|
||||||
|
ops.push(op)
|
||||||
|
str = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const computeDelta = () => {
|
||||||
|
// scope represents offset at current block from which we're intersted in picking string
|
||||||
|
// if it's -1 it means, we're out of scope and we should break at this point
|
||||||
|
let scope = start === null ? 0 : -1
|
||||||
|
loop: while (n !== null) {
|
||||||
|
if (scope < 0 && start !== null) {
|
||||||
|
if (start.client === n.id.client && start.clock >= n.id.clock && start.clock < n.id.clock + n.length) {
|
||||||
|
scope = n.id.clock + n.length - start.clock - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isVisible(n, snapshot) || (prevSnapshot !== undefined && isVisible(n, prevSnapshot))) {
|
||||||
|
switch (n.content.constructor) {
|
||||||
|
case ContentString: {
|
||||||
|
const cur = currentAttributes.get('ychange')
|
||||||
|
if (snapshot !== undefined && !isVisible(n, snapshot)) {
|
||||||
|
if (cur === undefined || cur.user !== n.id.client || cur.type !== 'removed') {
|
||||||
|
packStr()
|
||||||
|
currentAttributes.set('ychange', computeYChange ? computeYChange('removed', n.id) : { type: 'removed' })
|
||||||
|
}
|
||||||
|
} else if (prevSnapshot !== undefined && !isVisible(n, prevSnapshot)) {
|
||||||
|
if (cur === undefined || cur.user !== n.id.client || cur.type !== 'added') {
|
||||||
|
packStr()
|
||||||
|
currentAttributes.set('ychange', computeYChange ? computeYChange('added', n.id) : { type: 'added' })
|
||||||
|
}
|
||||||
|
} else if (cur !== undefined) {
|
||||||
|
packStr()
|
||||||
|
currentAttributes.delete('ychange')
|
||||||
|
}
|
||||||
|
let s = /** @type {ContentString} */ (n.content).str
|
||||||
|
if (scope > 0) {
|
||||||
|
str += s.slice(scope)
|
||||||
|
scope = 0
|
||||||
|
} else if (end !== null && end.client === n.id.client && end.clock >= n.id.clock && end.clock < n.id.clock + n.length) {
|
||||||
|
// we reached the end or range
|
||||||
|
const offset = n.id.clock + n.length - end.clock - 1
|
||||||
|
str += s.slice(0, s.length + offset) // scope is negative
|
||||||
|
packStr()
|
||||||
|
break loop
|
||||||
|
} else if (scope == 0) {
|
||||||
|
str += s
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case ContentType:
|
||||||
|
case ContentEmbed: {
|
||||||
|
packStr()
|
||||||
|
/**
|
||||||
|
* @type {Object<string,any>}
|
||||||
|
*/
|
||||||
|
const op = {
|
||||||
|
insert: n.content.getContent()[0]
|
||||||
|
}
|
||||||
|
if (currentAttributes.size > 0) {
|
||||||
|
const attrs = /** @type {Object<string,any>} */ ({})
|
||||||
|
op.attributes = attrs
|
||||||
|
currentAttributes.forEach((value, key) => {
|
||||||
|
attrs[key] = value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ops.push(op)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case ContentFormat:
|
||||||
|
if (isVisible(n, snapshot)) {
|
||||||
|
packStr()
|
||||||
|
updateCurrentAttributes(currentAttributes, /** @type {ContentFormat} */ (n.content))
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n = n.right
|
||||||
|
}
|
||||||
|
packStr()
|
||||||
|
}
|
||||||
|
if (snapshot || prevSnapshot) {
|
||||||
|
// snapshots are merged again after the transaction, so we need to keep the
|
||||||
|
// transaction alive until we are done
|
||||||
|
transact(doc, transaction => {
|
||||||
|
if (snapshot) {
|
||||||
|
splitSnapshotAffectedStructs(transaction, snapshot)
|
||||||
|
}
|
||||||
|
if (prevSnapshot) {
|
||||||
|
splitSnapshotAffectedStructs(transaction, prevSnapshot)
|
||||||
|
}
|
||||||
|
computeDelta()
|
||||||
|
}, 'cleanup')
|
||||||
|
} else {
|
||||||
|
computeDelta()
|
||||||
|
}
|
||||||
|
return ops
|
||||||
|
}
|
@ -13,7 +13,12 @@ import {
|
|||||||
readID,
|
readID,
|
||||||
RelativePosition,
|
RelativePosition,
|
||||||
ItemTextListPosition,
|
ItemTextListPosition,
|
||||||
ContentString
|
ContentString,
|
||||||
|
rangeDelta,
|
||||||
|
formatXmlString,
|
||||||
|
Snapshot,
|
||||||
|
YText,
|
||||||
|
YXmlText
|
||||||
} from "../internals.js"
|
} from "../internals.js"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,7 +92,7 @@ export class YWeakLink extends AbstractType {
|
|||||||
*
|
*
|
||||||
* @return {Array<any>}
|
* @return {Array<any>}
|
||||||
*/
|
*/
|
||||||
unqote () {
|
unquote () {
|
||||||
let result = /** @type {Array<any>} */ ([])
|
let result = /** @type {Array<any>} */ ([])
|
||||||
let item = this._firstItem
|
let item = this._firstItem
|
||||||
const end = /** @type {ID} */ (this._quoteEnd.item)
|
const end = /** @type {ID} */ (this._quoteEnd.item)
|
||||||
@ -191,6 +196,9 @@ export class YWeakLink extends AbstractType {
|
|||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
toString () {
|
toString () {
|
||||||
|
if (this._firstItem !== null) {
|
||||||
|
switch (/** @type {AbstractType<any>} */ (this._firstItem.parent).constructor) {
|
||||||
|
case YText:
|
||||||
let str = ''
|
let str = ''
|
||||||
/**
|
/**
|
||||||
* @type {Item|null}
|
* @type {Item|null}
|
||||||
@ -208,6 +216,30 @@ export class YWeakLink extends AbstractType {
|
|||||||
n = n.right
|
n = n.right
|
||||||
}
|
}
|
||||||
return str
|
return str
|
||||||
|
|
||||||
|
case YXmlText:
|
||||||
|
return this.toDelta().map(delta => formatXmlString(delta)).join('')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Delta representation of quoted part of underlying text type.
|
||||||
|
*
|
||||||
|
* @param {Snapshot|undefined} [snapshot]
|
||||||
|
* @param {Snapshot|undefined} [prevSnapshot]
|
||||||
|
* @param {function('removed' | 'added', ID):any} [computeYChange]
|
||||||
|
* @returns {Array<any>}
|
||||||
|
*/
|
||||||
|
toDelta(snapshot, prevSnapshot, computeYChange) {
|
||||||
|
if (this._firstItem !== null && this._quoteStart.item !== null && this._quoteEnd.item !== null) {
|
||||||
|
const parent = /** @type {AbstractType<any>} */ (this._firstItem.parent)
|
||||||
|
return rangeDelta(parent, this._quoteStart.item, this._quoteEnd.item, snapshot, prevSnapshot, computeYChange)
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,31 @@ export class YXmlText extends YText {
|
|||||||
|
|
||||||
toString () {
|
toString () {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return this.toDelta().map(delta => {
|
return this.toDelta().map(delta => formatXmlString(delta)).join('')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
toJSON () {
|
||||||
|
return this.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
||||||
|
*/
|
||||||
|
_write (encoder) {
|
||||||
|
encoder.writeTypeRef(YXmlTextRefID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats individual delta segment provided by `Text.toDelta` into XML-formatted string.
|
||||||
|
*
|
||||||
|
* @param {any} delta
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export const formatXmlString = (delta) => {
|
||||||
const nestedNodes = []
|
const nestedNodes = []
|
||||||
for (const nodeName in delta.attributes) {
|
for (const nodeName in delta.attributes) {
|
||||||
const attrs = []
|
const attrs = []
|
||||||
@ -93,22 +117,6 @@ export class YXmlText extends YText {
|
|||||||
str += `</${nestedNodes[i].nodeName}>`
|
str += `</${nestedNodes[i].nodeName}>`
|
||||||
}
|
}
|
||||||
return str
|
return str
|
||||||
}).join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {string}
|
|
||||||
*/
|
|
||||||
toJSON () {
|
|
||||||
return this.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {UpdateEncoderV1 | UpdateEncoderV2} encoder
|
|
||||||
*/
|
|
||||||
_write (encoder) {
|
|
||||||
encoder.writeTypeRef(YXmlTextRefID)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -52,7 +52,7 @@ export const testArrayQuoteMultipleElements = tc => {
|
|||||||
array0.insert(0, [array0.quote(1, 3)])
|
array0.insert(0, [array0.quote(1, 3)])
|
||||||
|
|
||||||
const link0 = array0.get(0)
|
const link0 = array0.get(0)
|
||||||
t.compare(link0.unqote(), [2, nested, 3])
|
t.compare(link0.unquote(), [2, nested, 3])
|
||||||
t.compare(array0.get(1), 1)
|
t.compare(array0.get(1), 1)
|
||||||
t.compare(array0.get(2), 2)
|
t.compare(array0.get(2), 2)
|
||||||
t.compare(array0.get(3), nested)
|
t.compare(array0.get(3), nested)
|
||||||
@ -61,26 +61,26 @@ export const testArrayQuoteMultipleElements = tc => {
|
|||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
|
|
||||||
const link1 = array1.get(0)
|
const link1 = array1.get(0)
|
||||||
let unqoted = link1.unqote()
|
let unquoted = link1.unquote()
|
||||||
t.compare(unqoted[0], 2)
|
t.compare(unquoted[0], 2)
|
||||||
t.compare(unqoted[1].toJSON(), {'key':'value'})
|
t.compare(unquoted[1].toJSON(), {'key':'value'})
|
||||||
t.compare(unqoted[2], 3)
|
t.compare(unquoted[2], 3)
|
||||||
t.compare(array1.get(1), 1)
|
t.compare(array1.get(1), 1)
|
||||||
t.compare(array1.get(2), 2)
|
t.compare(array1.get(2), 2)
|
||||||
t.compare(array1.get(3).toJSON(), {'key':'value'})
|
t.compare(array1.get(3).toJSON(), {'key':'value'})
|
||||||
t.compare(array1.get(4), 3)
|
t.compare(array1.get(4), 3)
|
||||||
|
|
||||||
array1.insert(3, ['A', 'B'])
|
array1.insert(3, ['A', 'B'])
|
||||||
unqoted = link1.unqote()
|
unquoted = link1.unquote()
|
||||||
t.compare(unqoted[0], 2)
|
t.compare(unquoted[0], 2)
|
||||||
t.compare(unqoted[1], 'A')
|
t.compare(unquoted[1], 'A')
|
||||||
t.compare(unqoted[2], 'B')
|
t.compare(unquoted[2], 'B')
|
||||||
t.compare(unqoted[3].toJSON(), {'key':'value'})
|
t.compare(unquoted[3].toJSON(), {'key':'value'})
|
||||||
t.compare(unqoted[4], 3)
|
t.compare(unquoted[4], 3)
|
||||||
|
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
|
|
||||||
t.compare(array0.get(0).unqote(), [2, 'A', 'B', nested, 3])
|
t.compare(array0.get(0).unquote(), [2, 'A', 'B', nested, 3])
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -92,7 +92,7 @@ export const testSelfQuotation = tc => {
|
|||||||
const link0 = array0.quote(0, 3)
|
const link0 = array0.quote(0, 3)
|
||||||
array0.insert(1, [link0]) // link is inserted into its own range
|
array0.insert(1, [link0]) // link is inserted into its own range
|
||||||
|
|
||||||
t.compare(link0.unqote(), [1, link0, 2, 3])
|
t.compare(link0.unquote(), [1, link0, 2, 3])
|
||||||
t.compare(array0.get(0), 1)
|
t.compare(array0.get(0), 1)
|
||||||
t.compare(array0.get(1), link0)
|
t.compare(array0.get(1), link0)
|
||||||
t.compare(array0.get(2), 2)
|
t.compare(array0.get(2), 2)
|
||||||
@ -102,8 +102,8 @@ export const testSelfQuotation = tc => {
|
|||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
|
|
||||||
const link1 = array1.get(1)
|
const link1 = array1.get(1)
|
||||||
let unqoted = link1.unqote()
|
let unquoted = link1.unquote()
|
||||||
t.compare(unqoted, [1, link1, 2, 3])
|
t.compare(unquoted, [1, link1, 2, 3])
|
||||||
t.compare(array1.get(0), 1)
|
t.compare(array1.get(0), 1)
|
||||||
t.compare(array1.get(1), link1)
|
t.compare(array1.get(1), link1)
|
||||||
t.compare(array1.get(2), 2)
|
t.compare(array1.get(2), 2)
|
||||||
@ -262,7 +262,7 @@ export const testObserveArray = tc => {
|
|||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
|
|
||||||
let link1 = /** @type {Y.WeakLink<String>} */ (array1.get(0))
|
let link1 = /** @type {Y.WeakLink<String>} */ (array1.get(0))
|
||||||
t.compare(link1.unqote(), ['B','C'])
|
t.compare(link1.unquote(), ['B','C'])
|
||||||
/**
|
/**
|
||||||
* @type {any}
|
* @type {any}
|
||||||
*/
|
*/
|
||||||
@ -270,16 +270,16 @@ export const testObserveArray = tc => {
|
|||||||
link1.observe((e) => target1 = e.target)
|
link1.observe((e) => target1 = e.target)
|
||||||
|
|
||||||
array0.delete(2)
|
array0.delete(2)
|
||||||
t.compare(target0.unqote(), ['C'])
|
t.compare(target0.unquote(), ['C'])
|
||||||
|
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
t.compare(target1.unqote(), ['C'])
|
t.compare(target1.unquote(), ['C'])
|
||||||
|
|
||||||
array1.delete(2)
|
array1.delete(2)
|
||||||
t.compare(target1.unqote(), [])
|
t.compare(target1.unquote(), [])
|
||||||
|
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
t.compare(target0.unqote(), [])
|
t.compare(target0.unquote(), [])
|
||||||
|
|
||||||
target0 = null
|
target0 = null
|
||||||
array0.delete(1)
|
array0.delete(1)
|
||||||
@ -682,41 +682,41 @@ export const testRemoteMapUpdate = tc => {
|
|||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
export const testTextBasic = tc => {
|
const testTextBasic = tc => {
|
||||||
const { testConnector, text0, array0, text1 } = init(tc, { users: 2 })
|
const { testConnector, text0, text1 } = init(tc, { users: 2 })
|
||||||
|
|
||||||
text0.insert(0, 'abcd')
|
text0.insert(0, 'abcd') // 'abcd'
|
||||||
const link0 = text0.quote(1, 2)
|
const link0 = text0.quote(1, 2) // quote: [bc]
|
||||||
t.compare(link0.toString(), 'bc')
|
t.compare(link0.toString(), 'bc')
|
||||||
text0.insert(2, 'ef')
|
text0.insert(2, 'ef') // 'abefcd', quote: [befc]
|
||||||
t.compare(link0.toString(), 'befc')
|
t.compare(link0.toString(), 'befc')
|
||||||
text0.delete(3, 3)
|
text0.delete(3, 3) // 'abe', quote: [be]
|
||||||
t.compare(link0.toString(), 'be')
|
t.compare(link0.toString(), 'be')
|
||||||
text0.insertEmbed(3, link0)
|
text0.insertEmbed(3, link0) // 'abe[be]'
|
||||||
|
|
||||||
testConnector.flushAllMessages()
|
testConnector.flushAllMessages()
|
||||||
|
|
||||||
const delta = text1.toDelta()
|
const delta = text1.toDelta()
|
||||||
const { insert } = delta[1]
|
const { insert } = delta[1] // YWeakLink
|
||||||
t.compare(insert.toString(), 'be')
|
t.compare(insert.toString(), 'be')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {t.TestCase} tc
|
* @param {t.TestCase} tc
|
||||||
*/
|
*/
|
||||||
const testQuoteFormattedText = tc => {
|
export const testQuoteFormattedText = tc => {
|
||||||
const doc = new Y.Doc()
|
const doc = new Y.Doc()
|
||||||
const text = /** @type {Y.XmlText} */ (doc.get('text', Y.XmlText))
|
const text = /** @type {Y.XmlText} */ (doc.get('text', Y.XmlText))
|
||||||
const text2 = /** @type {Y.XmlText} */ (doc.get('text2', Y.XmlText))
|
const text2 = /** @type {Y.XmlText} */ (doc.get('text2', Y.XmlText))
|
||||||
|
|
||||||
text.insert(0, 'abcde')
|
text.insert(0, 'abcde')
|
||||||
text.format(1, 3, {i:true}) // 'a<i>bcd</i>e'
|
text.format(0, 1, {b:true})
|
||||||
const l1 = text.quote(0, 2) // 'a<i>b</i>'
|
text.format(1, 3, {i:true}) // '<b>a</b><i>bcd</i>e'
|
||||||
|
const l1 = text.quote(0, 2)
|
||||||
|
t.compare(l1.toString(), '<b>a</b><i>b</i>')
|
||||||
const l2 = text.quote(2, 1) // '<i>c</i>'
|
const l2 = text.quote(2, 1) // '<i>c</i>'
|
||||||
const l3 = text.quote(3, 2) // '<i>d</i>e'
|
|
||||||
|
|
||||||
t.compare(l1.toString(), 'a<i>b</i>')
|
|
||||||
t.compare(l2.toString(), '<i>c</i>')
|
t.compare(l2.toString(), '<i>c</i>')
|
||||||
|
const l3 = text.quote(3, 2) // '<i>d</i>e'
|
||||||
t.compare(l3.toString(), '<i>d</i>e')
|
t.compare(l3.toString(), '<i>d</i>e')
|
||||||
|
|
||||||
text2.insertEmbed(0, l1)
|
text2.insertEmbed(0, l1)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user