Include Item.redone in binary encoding
				
					
				
			This commit is contained in:
		
							parent
							
								
									384ec4db78
								
							
						
					
					
						commit
						1cfc865045
					
				@ -93,6 +93,7 @@ export const splitItem = (transaction, leftItem, diff) => {
 | 
			
		||||
    leftItem.rightOrigin,
 | 
			
		||||
    leftItem.parent,
 | 
			
		||||
    leftItem.parentSub,
 | 
			
		||||
    leftItem.redone !== null ? createID(leftItem.redone.client, leftItem.redone.clock + diff) : null,
 | 
			
		||||
    leftItem.content.splice(diff)
 | 
			
		||||
  )
 | 
			
		||||
  if (leftItem.deleted) {
 | 
			
		||||
@ -101,9 +102,6 @@ export const splitItem = (transaction, leftItem, diff) => {
 | 
			
		||||
  if (leftItem.keep) {
 | 
			
		||||
    rightItem.keep = true
 | 
			
		||||
  }
 | 
			
		||||
  if (leftItem.redone !== null) {
 | 
			
		||||
    rightItem.redone = createID(leftItem.redone.client, leftItem.redone.clock + diff)
 | 
			
		||||
  }
 | 
			
		||||
  // update left (do not set leftItem.rightOrigin as it will lead to problems when syncing)
 | 
			
		||||
  leftItem.right = rightItem
 | 
			
		||||
  // update right
 | 
			
		||||
@ -232,6 +230,7 @@ export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemo
 | 
			
		||||
    right, right && right.id,
 | 
			
		||||
    parentType,
 | 
			
		||||
    item.parentSub,
 | 
			
		||||
    null,
 | 
			
		||||
    item.content.copy()
 | 
			
		||||
  )
 | 
			
		||||
  item.redone = nextId
 | 
			
		||||
@ -252,9 +251,10 @@ export class Item extends AbstractStruct {
 | 
			
		||||
   * @param {ID | null} rightOrigin
 | 
			
		||||
   * @param {AbstractType<any>|ID|null} parent Is a type if integrated, is null if it is possible to copy parent from left or right, is ID before integration to search for it.
 | 
			
		||||
   * @param {string | null} parentSub
 | 
			
		||||
   * @param {ID | null} redone
 | 
			
		||||
   * @param {AbstractContent} content
 | 
			
		||||
   */
 | 
			
		||||
  constructor (id, left, origin, right, rightOrigin, parent, parentSub, content) {
 | 
			
		||||
  constructor (id, left, origin, right, rightOrigin, parent, parentSub, redone, content) {
 | 
			
		||||
    super(id, content.getLength())
 | 
			
		||||
    /**
 | 
			
		||||
     * The item that was originally to the left of this item.
 | 
			
		||||
@ -293,7 +293,7 @@ export class Item extends AbstractStruct {
 | 
			
		||||
     * this operation.
 | 
			
		||||
     * @type {ID | null}
 | 
			
		||||
     */
 | 
			
		||||
    this.redone = null
 | 
			
		||||
    this.redone = redone
 | 
			
		||||
    /**
 | 
			
		||||
     * @type {AbstractContent}
 | 
			
		||||
     */
 | 
			
		||||
@ -653,10 +653,11 @@ export class Item extends AbstractStruct {
 | 
			
		||||
    const origin = offset > 0 ? createID(this.id.client, this.id.clock + offset - 1) : this.origin
 | 
			
		||||
    const rightOrigin = this.rightOrigin
 | 
			
		||||
    const parentSub = this.parentSub
 | 
			
		||||
    const info = (this.content.getRef() & binary.BITS5) |
 | 
			
		||||
    const info = (this.content.getRef() & binary.BITS4) |
 | 
			
		||||
      (origin === null ? 0 : binary.BIT8) | // origin is defined
 | 
			
		||||
      (rightOrigin === null ? 0 : binary.BIT7) | // right origin is defined
 | 
			
		||||
      (parentSub === null ? 0 : binary.BIT6) // parentSub is non-null
 | 
			
		||||
      (parentSub === null ? 0 : binary.BIT6) | // parentSub is non-null
 | 
			
		||||
      (this.redone === null ? 0 : binary.BIT5) // redone is defined
 | 
			
		||||
    encoder.writeInfo(info)
 | 
			
		||||
    if (origin !== null) {
 | 
			
		||||
      encoder.writeLeftID(origin)
 | 
			
		||||
@ -691,6 +692,9 @@ export class Item extends AbstractStruct {
 | 
			
		||||
        encoder.writeString(parentSub)
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (this.redone !== null) {
 | 
			
		||||
      encoder.writeRedone(this.redone)
 | 
			
		||||
    }
 | 
			
		||||
    this.content.write(encoder, offset)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -699,7 +703,7 @@ export class Item extends AbstractStruct {
 | 
			
		||||
 * @param {UpdateDecoderV1 | UpdateDecoderV2} decoder
 | 
			
		||||
 * @param {number} info
 | 
			
		||||
 */
 | 
			
		||||
export const readItemContent = (decoder, info) => contentRefs[info & binary.BITS5](decoder)
 | 
			
		||||
export const readItemContent = (decoder, info) => contentRefs[info & binary.BITS4](decoder)
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A lookup map for reading Item content.
 | 
			
		||||
 | 
			
		||||
@ -643,7 +643,7 @@ export const typeListInsertGenericsAfter = (transaction, parent, referenceItem,
 | 
			
		||||
  let jsonContent = []
 | 
			
		||||
  const packJsonContent = () => {
 | 
			
		||||
    if (jsonContent.length > 0) {
 | 
			
		||||
      left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentAny(jsonContent))
 | 
			
		||||
      left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, null, new ContentAny(jsonContent))
 | 
			
		||||
      left.integrate(transaction, 0)
 | 
			
		||||
      jsonContent = []
 | 
			
		||||
    }
 | 
			
		||||
@ -665,16 +665,16 @@ export const typeListInsertGenericsAfter = (transaction, parent, referenceItem,
 | 
			
		||||
          switch (c.constructor) {
 | 
			
		||||
            case Uint8Array:
 | 
			
		||||
            case ArrayBuffer:
 | 
			
		||||
              left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentBinary(new Uint8Array(/** @type {Uint8Array} */ (c))))
 | 
			
		||||
              left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, null, new ContentBinary(new Uint8Array(/** @type {Uint8Array} */ (c))))
 | 
			
		||||
              left.integrate(transaction, 0)
 | 
			
		||||
              break
 | 
			
		||||
            case Doc:
 | 
			
		||||
              left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentDoc(/** @type {Doc} */ (c)))
 | 
			
		||||
              left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, null, new ContentDoc(/** @type {Doc} */ (c)))
 | 
			
		||||
              left.integrate(transaction, 0)
 | 
			
		||||
              break
 | 
			
		||||
            default:
 | 
			
		||||
              if (c instanceof AbstractType) {
 | 
			
		||||
                left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentType(c))
 | 
			
		||||
                left = new Item(createID(ownClientId, getState(store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, null, new ContentType(c))
 | 
			
		||||
                left.integrate(transaction, 0)
 | 
			
		||||
              } else {
 | 
			
		||||
                throw new Error('Unexpected content type in insert operation')
 | 
			
		||||
@ -862,7 +862,7 @@ export const typeMapSet = (transaction, parent, key, value) => {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  new Item(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, null, null, parent, key, content).integrate(transaction, 0)
 | 
			
		||||
  new Item(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, null, null, parent, key, null, content).integrate(transaction, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 | 
			
		||||
@ -166,7 +166,7 @@ const insertNegatedAttributes = (transaction, parent, currPos, negatedAttributes
 | 
			
		||||
  negatedAttributes.forEach((val, key) => {
 | 
			
		||||
    const left = currPos.left
 | 
			
		||||
    const right = currPos.right
 | 
			
		||||
    const nextFormat = new Item(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentFormat(key, val))
 | 
			
		||||
    const nextFormat = new Item(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, null, new ContentFormat(key, val))
 | 
			
		||||
    nextFormat.integrate(transaction, 0)
 | 
			
		||||
    currPos.right = nextFormat
 | 
			
		||||
    currPos.forward()
 | 
			
		||||
@ -232,7 +232,7 @@ const insertAttributes = (transaction, parent, currPos, attributes) => {
 | 
			
		||||
      // save negated attribute (set null if currentVal undefined)
 | 
			
		||||
      negatedAttributes.set(key, currentVal)
 | 
			
		||||
      const { left, right } = currPos
 | 
			
		||||
      currPos.right = new Item(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, new ContentFormat(key, val))
 | 
			
		||||
      currPos.right = new Item(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, null, new ContentFormat(key, val))
 | 
			
		||||
      currPos.right.integrate(transaction, 0)
 | 
			
		||||
      currPos.forward()
 | 
			
		||||
    }
 | 
			
		||||
@ -266,7 +266,7 @@ const insertText = (transaction, parent, currPos, text, attributes) => {
 | 
			
		||||
  if (parent._searchMarker) {
 | 
			
		||||
    updateMarkerChanges(parent._searchMarker, currPos.index, content.getLength())
 | 
			
		||||
  }
 | 
			
		||||
  right = new Item(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, content)
 | 
			
		||||
  right = new Item(createID(ownClientId, getState(doc.store, ownClientId)), left, left && left.lastId, right, right && right.id, parent, null, null, content)
 | 
			
		||||
  right.integrate(transaction, 0)
 | 
			
		||||
  currPos.right = right
 | 
			
		||||
  currPos.index = index
 | 
			
		||||
@ -342,7 +342,7 @@ const formatText = (transaction, parent, currPos, length, attributes) => {
 | 
			
		||||
    for (; length > 0; length--) {
 | 
			
		||||
      newlines += '\n'
 | 
			
		||||
    }
 | 
			
		||||
    currPos.right = new Item(createID(ownClientId, getState(doc.store, ownClientId)), currPos.left, currPos.left && currPos.left.lastId, currPos.right, currPos.right && currPos.right.id, parent, null, new ContentString(newlines))
 | 
			
		||||
    currPos.right = new Item(createID(ownClientId, getState(doc.store, ownClientId)), currPos.left, currPos.left && currPos.left.lastId, currPos.right, currPos.right && currPos.right.id, parent, null, null, new ContentString(newlines))
 | 
			
		||||
    currPos.right.integrate(transaction, 0)
 | 
			
		||||
    currPos.forward()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -46,6 +46,13 @@ export class UpdateDecoderV1 extends DSDecoderV1 {
 | 
			
		||||
    return createID(decoding.readVarUint(this.restDecoder), decoding.readVarUint(this.restDecoder))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @return {ID}
 | 
			
		||||
   */
 | 
			
		||||
  readRedone () {
 | 
			
		||||
    return createID(decoding.readVarUint(this.restDecoder), decoding.readVarUint(this.restDecoder))
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Read the next client id.
 | 
			
		||||
   * Use this in favor of readID whenever possible to reduce the number of objects created.
 | 
			
		||||
@ -174,6 +181,7 @@ export class UpdateDecoderV2 extends DSDecoderV2 {
 | 
			
		||||
    this.clientDecoder = new decoding.UintOptRleDecoder(decoding.readVarUint8Array(decoder))
 | 
			
		||||
    this.leftClockDecoder = new decoding.IntDiffOptRleDecoder(decoding.readVarUint8Array(decoder))
 | 
			
		||||
    this.rightClockDecoder = new decoding.IntDiffOptRleDecoder(decoding.readVarUint8Array(decoder))
 | 
			
		||||
    this.redoneClockDecoder = new decoding.IntDiffOptRleDecoder(decoding.readVarUint8Array(decoder))
 | 
			
		||||
    this.infoDecoder = new decoding.RleDecoder(decoding.readVarUint8Array(decoder), decoding.readUint8)
 | 
			
		||||
    this.stringDecoder = new decoding.StringDecoder(decoding.readVarUint8Array(decoder))
 | 
			
		||||
    this.parentInfoDecoder = new decoding.RleDecoder(decoding.readVarUint8Array(decoder), decoding.readUint8)
 | 
			
		||||
@ -195,6 +203,13 @@ export class UpdateDecoderV2 extends DSDecoderV2 {
 | 
			
		||||
    return new ID(this.clientDecoder.read(), this.rightClockDecoder.read())
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @return {ID}
 | 
			
		||||
   */
 | 
			
		||||
  readRedone () {
 | 
			
		||||
    return new ID(this.clientDecoder.read(), this.redoneClockDecoder.read())
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Read the next client id.
 | 
			
		||||
   * Use this in favor of readID whenever possible to reduce the number of objects created.
 | 
			
		||||
 | 
			
		||||
@ -50,6 +50,14 @@ export class UpdateEncoderV1 extends DSEncoderV1 {
 | 
			
		||||
    encoding.writeVarUint(this.restEncoder, id.clock)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @param {ID} id
 | 
			
		||||
   */
 | 
			
		||||
  writeRedone (id) {
 | 
			
		||||
    encoding.writeVarUint(this.restEncoder, id.client)
 | 
			
		||||
    encoding.writeVarUint(this.restEncoder, id.clock)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Use writeClient and writeClock instead of writeID if possible.
 | 
			
		||||
   * @param {number} client
 | 
			
		||||
@ -177,6 +185,7 @@ export class UpdateEncoderV2 extends DSEncoderV2 {
 | 
			
		||||
    this.clientEncoder = new encoding.UintOptRleEncoder()
 | 
			
		||||
    this.leftClockEncoder = new encoding.IntDiffOptRleEncoder()
 | 
			
		||||
    this.rightClockEncoder = new encoding.IntDiffOptRleEncoder()
 | 
			
		||||
    this.redoneClockEncoder = new encoding.IntDiffOptRleEncoder()
 | 
			
		||||
    this.infoEncoder = new encoding.RleEncoder(encoding.writeUint8)
 | 
			
		||||
    this.stringEncoder = new encoding.StringEncoder()
 | 
			
		||||
    this.parentInfoEncoder = new encoding.RleEncoder(encoding.writeUint8)
 | 
			
		||||
@ -191,6 +200,7 @@ export class UpdateEncoderV2 extends DSEncoderV2 {
 | 
			
		||||
    encoding.writeVarUint8Array(encoder, this.clientEncoder.toUint8Array())
 | 
			
		||||
    encoding.writeVarUint8Array(encoder, this.leftClockEncoder.toUint8Array())
 | 
			
		||||
    encoding.writeVarUint8Array(encoder, this.rightClockEncoder.toUint8Array())
 | 
			
		||||
    encoding.writeVarUint8Array(encoder, this.redoneClockEncoder.toUint8Array())
 | 
			
		||||
    encoding.writeVarUint8Array(encoder, encoding.toUint8Array(this.infoEncoder))
 | 
			
		||||
    encoding.writeVarUint8Array(encoder, this.stringEncoder.toUint8Array())
 | 
			
		||||
    encoding.writeVarUint8Array(encoder, encoding.toUint8Array(this.parentInfoEncoder))
 | 
			
		||||
@ -217,6 +227,14 @@ export class UpdateEncoderV2 extends DSEncoderV2 {
 | 
			
		||||
    this.rightClockEncoder.write(id.clock)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @param {ID} id
 | 
			
		||||
   */
 | 
			
		||||
  writeRedone (id) {
 | 
			
		||||
    this.clientEncoder.write(id.client)
 | 
			
		||||
    this.redoneClockEncoder.write(id.clock)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * @param {number} client
 | 
			
		||||
   */
 | 
			
		||||
 | 
			
		||||
@ -127,7 +127,7 @@ export const readClientsStructRefs = (decoder, doc) => {
 | 
			
		||||
    clientRefs.set(client, { i: 0, refs })
 | 
			
		||||
    for (let i = 0; i < numberOfStructs; i++) {
 | 
			
		||||
      const info = decoder.readInfo()
 | 
			
		||||
      switch (binary.BITS5 & info) {
 | 
			
		||||
      switch (binary.BITS4 & info) {
 | 
			
		||||
        case 0: { // GC
 | 
			
		||||
          const len = decoder.readLen()
 | 
			
		||||
          refs[i] = new GC(createID(client, clock), len)
 | 
			
		||||
@ -160,6 +160,7 @@ export const readClientsStructRefs = (decoder, doc) => {
 | 
			
		||||
            (info & binary.BIT7) === binary.BIT7 ? decoder.readRightID() : null, // right origin
 | 
			
		||||
            cantCopyParentInfo ? (decoder.readParentInfo() ? doc.get(decoder.readString()) : decoder.readLeftID()) : null, // parent
 | 
			
		||||
            cantCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoder.readString() : null, // parentSub
 | 
			
		||||
            (info & binary.BIT5) === binary.BIT5 ? decoder.readRedone() : null, // redone
 | 
			
		||||
            readItemContent(decoder, info) // item content
 | 
			
		||||
          )
 | 
			
		||||
          /* A non-optimized implementation of the above algorithm:
 | 
			
		||||
@ -184,6 +185,7 @@ export const readClientsStructRefs = (decoder, doc) => {
 | 
			
		||||
            rightOrigin, // right origin
 | 
			
		||||
            cantCopyParentInfo && !hasParentYKey ? decoder.readLeftID() : (parentYKey !== null ? doc.get(parentYKey) : null), // parent
 | 
			
		||||
            cantCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoder.readString() : null, // parentSub
 | 
			
		||||
            (info & binary.BIT5) === binary.BIT5 ? decoder.readRedone() : null, // redone
 | 
			
		||||
            readItemContent(decoder, info) // item content
 | 
			
		||||
          )
 | 
			
		||||
          */
 | 
			
		||||
 | 
			
		||||
@ -53,7 +53,7 @@ function * lazyStructReaderGenerator (decoder) {
 | 
			
		||||
        const len = decoding.readVarUint(decoder.restDecoder)
 | 
			
		||||
        yield new Skip(createID(client, clock), len)
 | 
			
		||||
        clock += len
 | 
			
		||||
      } else if ((binary.BITS5 & info) !== 0) {
 | 
			
		||||
      } else if ((binary.BITS4 & info) !== 0) {
 | 
			
		||||
        const cantCopyParentInfo = (info & (binary.BIT7 | binary.BIT8)) === 0
 | 
			
		||||
        // If parent = null and neither left nor right are defined, then we know that `parent` is child of `y`
 | 
			
		||||
        // and we read the next string as parentYKey.
 | 
			
		||||
@ -68,6 +68,7 @@ function * lazyStructReaderGenerator (decoder) {
 | 
			
		||||
          // @ts-ignore Force writing a string here.
 | 
			
		||||
          cantCopyParentInfo ? (decoder.readParentInfo() ? decoder.readString() : decoder.readLeftID()) : null, // parent
 | 
			
		||||
          cantCopyParentInfo && (info & binary.BIT6) === binary.BIT6 ? decoder.readString() : null, // parentSub
 | 
			
		||||
          (info & binary.BIT5) === binary.BIT5 ? decoder.readRedone() : null, // redone
 | 
			
		||||
          readItemContent(decoder, info) // item content
 | 
			
		||||
        )
 | 
			
		||||
        yield struct
 | 
			
		||||
@ -316,6 +317,7 @@ const sliceStruct = (left, diff) => {
 | 
			
		||||
      leftItem.rightOrigin,
 | 
			
		||||
      leftItem.parent,
 | 
			
		||||
      leftItem.parentSub,
 | 
			
		||||
      null,
 | 
			
		||||
      leftItem.content.splice(diff)
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -120,6 +120,6 @@ export const testRelativePositionWithUndo = tc => {
 | 
			
		||||
  t.assert(Y.createAbsolutePositionFromRelativePosition(rpos, ydoc, false)?.index === 6)
 | 
			
		||||
  const ydocClone = new Y.Doc()
 | 
			
		||||
  Y.applyUpdate(ydocClone, Y.encodeStateAsUpdate(ydoc))
 | 
			
		||||
  t.assert(Y.createAbsolutePositionFromRelativePosition(rpos, ydocClone)?.index === 6)
 | 
			
		||||
  t.assert(Y.createAbsolutePositionFromRelativePosition(rpos, ydocClone)?.index === 1)
 | 
			
		||||
  t.assert(Y.createAbsolutePositionFromRelativePosition(rpos, ydocClone, false)?.index === 6)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2079,7 +2079,7 @@ export const testBestCase = _tc => {
 | 
			
		||||
      /**
 | 
			
		||||
       * @type {Y.Item}
 | 
			
		||||
       */
 | 
			
		||||
      const n = new Y.Item(Y.createID(0, 0), null, null, null, null, null, null, c)
 | 
			
		||||
      const n = new Y.Item(Y.createID(0, 0), null, null, null, null, null, null, null, c)
 | 
			
		||||
      // items.push(n)
 | 
			
		||||
      items[i] = n
 | 
			
		||||
      n.right = prevItem
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user