UndoManager fixes
This commit is contained in:
		
							parent
							
								
									952a9b2c41
								
							
						
					
					
						commit
						e376b5d472
					
				@ -42,5 +42,6 @@ export {
 | 
				
			|||||||
  iterateDeletedStructs,
 | 
					  iterateDeletedStructs,
 | 
				
			||||||
  applyUpdate,
 | 
					  applyUpdate,
 | 
				
			||||||
  encodeStateAsUpdate,
 | 
					  encodeStateAsUpdate,
 | 
				
			||||||
  encodeStateVector
 | 
					  encodeStateVector,
 | 
				
			||||||
 | 
					  UndoManager
 | 
				
			||||||
} from './internals.js'
 | 
					} from './internals.js'
 | 
				
			||||||
 | 
				
			|||||||
@ -33,6 +33,31 @@ import * as maplib from 'lib0/map.js'
 | 
				
			|||||||
import * as set from 'lib0/set.js'
 | 
					import * as set from 'lib0/set.js'
 | 
				
			||||||
import * as binary from 'lib0/binary.js'
 | 
					import * as binary from 'lib0/binary.js'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * @param {StructStore} store
 | 
				
			||||||
 | 
					 * @param {ID} id
 | 
				
			||||||
 | 
					 * @return {{item:Item, diff:number}}
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					export const followRedone = (store, id) => {
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * @type {ID|null}
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  let nextID = id
 | 
				
			||||||
 | 
					  let diff = 0
 | 
				
			||||||
 | 
					  let item
 | 
				
			||||||
 | 
					  do {
 | 
				
			||||||
 | 
					    if (diff > 0) {
 | 
				
			||||||
 | 
					      nextID = createID(nextID.client, nextID.clock + diff)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    item = getItem(store, nextID)
 | 
				
			||||||
 | 
					    diff = nextID.clock - item.id.clock
 | 
				
			||||||
 | 
					    nextID = item.redone
 | 
				
			||||||
 | 
					  } while (nextID !== null)
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    item, diff
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Make sure that neither item nor any of its parents is ever deleted.
 | 
					 * Make sure that neither item nor any of its parents is ever deleted.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
@ -77,6 +102,9 @@ export const splitItem = (transaction, leftItem, diff) => {
 | 
				
			|||||||
  if (leftItem.keep) {
 | 
					  if (leftItem.keep) {
 | 
				
			||||||
    rightItem.keep = true
 | 
					    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)
 | 
					  // update left (do not set leftItem.rightOrigin as it will lead to problems when syncing)
 | 
				
			||||||
  leftItem.right = rightItem
 | 
					  leftItem.right = rightItem
 | 
				
			||||||
  // update right
 | 
					  // update right
 | 
				
			||||||
@ -106,7 +134,7 @@ export const splitItem = (transaction, leftItem, diff) => {
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
export const redoItem = (transaction, item, redoitems) => {
 | 
					export const redoItem = (transaction, item, redoitems) => {
 | 
				
			||||||
  if (item.redone !== null) {
 | 
					  if (item.redone !== null) {
 | 
				
			||||||
    return item.redone
 | 
					    return getItemCleanStart(transaction, transaction.doc.store, item.redone)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  let parentItem = item.parent._item
 | 
					  let parentItem = item.parent._item
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@ -146,7 +174,7 @@ export const redoItem = (transaction, item, redoitems) => {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  if (parentItem !== null && parentItem.redone !== null) {
 | 
					  if (parentItem !== null && parentItem.redone !== null) {
 | 
				
			||||||
    while (parentItem.redone !== null) {
 | 
					    while (parentItem.redone !== null) {
 | 
				
			||||||
      parentItem = parentItem.redone
 | 
					      parentItem = getItemCleanStart(transaction, transaction.doc.store, parentItem.redone)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    // find next cloned_redo items
 | 
					    // find next cloned_redo items
 | 
				
			||||||
    while (left !== null) {
 | 
					    while (left !== null) {
 | 
				
			||||||
@ -156,7 +184,7 @@ export const redoItem = (transaction, item, redoitems) => {
 | 
				
			|||||||
      let leftTrace = left
 | 
					      let leftTrace = left
 | 
				
			||||||
      // trace redone until parent matches
 | 
					      // trace redone until parent matches
 | 
				
			||||||
      while (leftTrace !== null && leftTrace.parent._item !== parentItem) {
 | 
					      while (leftTrace !== null && leftTrace.parent._item !== parentItem) {
 | 
				
			||||||
        leftTrace = leftTrace.redone
 | 
					        leftTrace = leftTrace.redone === null ? null : getItemCleanStart(transaction, transaction.doc.store, leftTrace.redone)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (leftTrace !== null && leftTrace.parent._item === parentItem) {
 | 
					      if (leftTrace !== null && leftTrace.parent._item === parentItem) {
 | 
				
			||||||
        left = leftTrace
 | 
					        left = leftTrace
 | 
				
			||||||
@ -171,7 +199,7 @@ export const redoItem = (transaction, item, redoitems) => {
 | 
				
			|||||||
      let rightTrace = right
 | 
					      let rightTrace = right
 | 
				
			||||||
      // trace redone until parent matches
 | 
					      // trace redone until parent matches
 | 
				
			||||||
      while (rightTrace !== null && rightTrace.parent._item !== parentItem) {
 | 
					      while (rightTrace !== null && rightTrace.parent._item !== parentItem) {
 | 
				
			||||||
        rightTrace = rightTrace.redone
 | 
					        rightTrace = rightTrace.redone === null ? null : getItemCleanStart(transaction, transaction.doc.store, rightTrace.redone)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (rightTrace !== null && rightTrace.parent._item === parentItem) {
 | 
					      if (rightTrace !== null && rightTrace.parent._item === parentItem) {
 | 
				
			||||||
        right = rightTrace
 | 
					        right = rightTrace
 | 
				
			||||||
@ -188,7 +216,8 @@ export const redoItem = (transaction, item, redoitems) => {
 | 
				
			|||||||
    item.parentSub,
 | 
					    item.parentSub,
 | 
				
			||||||
    item.content.copy()
 | 
					    item.content.copy()
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
  item.redone = redoneItem
 | 
					  item.redone = redoneItem.id
 | 
				
			||||||
 | 
					  keepItem(redoneItem)
 | 
				
			||||||
  redoneItem.integrate(transaction)
 | 
					  redoneItem.integrate(transaction)
 | 
				
			||||||
  return redoneItem
 | 
					  return redoneItem
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -254,7 +283,7 @@ export class Item extends AbstractStruct {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * If this type's effect is reundone this type refers to the type that undid
 | 
					     * If this type's effect is reundone this type refers to the type that undid
 | 
				
			||||||
     * this operation.
 | 
					     * this operation.
 | 
				
			||||||
     * @type {Item | null}
 | 
					     * @type {ID | null}
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    this.redone = null
 | 
					    this.redone = null
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,5 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  getItem,
 | 
					 | 
				
			||||||
  createID,
 | 
					  createID,
 | 
				
			||||||
  writeID,
 | 
					  writeID,
 | 
				
			||||||
  readID,
 | 
					  readID,
 | 
				
			||||||
@ -9,6 +8,7 @@ import {
 | 
				
			|||||||
  findRootTypeKey,
 | 
					  findRootTypeKey,
 | 
				
			||||||
  Item,
 | 
					  Item,
 | 
				
			||||||
  ContentType,
 | 
					  ContentType,
 | 
				
			||||||
 | 
					  followRedone,
 | 
				
			||||||
  ID, Doc, AbstractType // eslint-disable-line
 | 
					  ID, Doc, AbstractType // eslint-disable-line
 | 
				
			||||||
} from '../internals.js'
 | 
					} from '../internals.js'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -222,11 +222,14 @@ export const createAbsolutePositionFromRelativePosition = (rpos, doc) => {
 | 
				
			|||||||
    if (getState(store, rightID.client) <= rightID.clock) {
 | 
					    if (getState(store, rightID.client) <= rightID.clock) {
 | 
				
			||||||
      return null
 | 
					      return null
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    const right = getItem(store, rightID)
 | 
					    const res = followRedone(store, rightID)
 | 
				
			||||||
 | 
					    const right = res.item
 | 
				
			||||||
    if (!(right instanceof Item)) {
 | 
					    if (!(right instanceof Item)) {
 | 
				
			||||||
      return null
 | 
					      return null
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    index = right.deleted || !right.countable ? 0 : rightID.clock - right.id.clock
 | 
					    type = right.parent
 | 
				
			||||||
 | 
					    if (type._item !== null && !type._item.deleted) {
 | 
				
			||||||
 | 
					      index = right.deleted || !right.countable ? 0 : res.diff
 | 
				
			||||||
      let n = right.left
 | 
					      let n = right.left
 | 
				
			||||||
      while (n !== null) {
 | 
					      while (n !== null) {
 | 
				
			||||||
        if (!n.deleted && n.countable) {
 | 
					        if (!n.deleted && n.countable) {
 | 
				
			||||||
@ -234,7 +237,7 @@ export const createAbsolutePositionFromRelativePosition = (rpos, doc) => {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        n = n.left
 | 
					        n = n.left
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    type = right.parent
 | 
					    }
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    if (tname !== null) {
 | 
					    if (tname !== null) {
 | 
				
			||||||
      type = doc.get(tname)
 | 
					      type = doc.get(tname)
 | 
				
			||||||
@ -243,9 +246,9 @@ export const createAbsolutePositionFromRelativePosition = (rpos, doc) => {
 | 
				
			|||||||
        // type does not exist yet
 | 
					        // type does not exist yet
 | 
				
			||||||
        return null
 | 
					        return null
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      const struct = getItem(store, typeID)
 | 
					      const { item } = followRedone(store, typeID)
 | 
				
			||||||
      if (struct instanceof Item && struct.content instanceof ContentType) {
 | 
					      if (item instanceof Item && item.content instanceof ContentType) {
 | 
				
			||||||
        type = struct.content.type
 | 
					        type = item.content.type
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        // struct is garbage collected
 | 
					        // struct is garbage collected
 | 
				
			||||||
        return null
 | 
					        return null
 | 
				
			||||||
@ -255,9 +258,6 @@ export const createAbsolutePositionFromRelativePosition = (rpos, doc) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    index = type._length
 | 
					    index = type._length
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  if (type._item !== null && type._item.deleted) {
 | 
					 | 
				
			||||||
    return null
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return createAbsolutePosition(type, index)
 | 
					  return createAbsolutePosition(type, index)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -6,11 +6,14 @@ import {
 | 
				
			|||||||
  redoItem,
 | 
					  redoItem,
 | 
				
			||||||
  iterateStructs,
 | 
					  iterateStructs,
 | 
				
			||||||
  isParentOf,
 | 
					  isParentOf,
 | 
				
			||||||
 | 
					  createID,
 | 
				
			||||||
 | 
					  followRedone,
 | 
				
			||||||
 | 
					  getItemCleanStart,
 | 
				
			||||||
  Doc, Item, GC, DeleteSet, AbstractType // eslint-disable-line
 | 
					  Doc, Item, GC, DeleteSet, AbstractType // eslint-disable-line
 | 
				
			||||||
} from '../internals.js'
 | 
					} from '../internals.js'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as time from 'lib0/time.js'
 | 
					import * as time from 'lib0/time.js'
 | 
				
			||||||
import { Observable } from 'lib0/observable'
 | 
					import { Observable } from 'lib0/observable.js'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class StackItem {
 | 
					class StackItem {
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@ -59,17 +62,27 @@ const popStackItem = (undoManager, stack, eventType) => {
 | 
				
			|||||||
      })
 | 
					      })
 | 
				
			||||||
      const structs = /** @type {Array<GC|Item>} */ (store.clients.get(doc.clientID))
 | 
					      const structs = /** @type {Array<GC|Item>} */ (store.clients.get(doc.clientID))
 | 
				
			||||||
      iterateStructs(transaction, structs, stackItem.start, stackItem.len, struct => {
 | 
					      iterateStructs(transaction, structs, stackItem.start, stackItem.len, struct => {
 | 
				
			||||||
 | 
					        if (struct instanceof Item && struct.redone !== null) {
 | 
				
			||||||
 | 
					          let { item, diff } = followRedone(store, struct.id)
 | 
				
			||||||
 | 
					          if (diff > 0) {
 | 
				
			||||||
 | 
					            item = getItemCleanStart(transaction, store, struct.id)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (item.length > stackItem.len) {
 | 
				
			||||||
 | 
					            getItemCleanStart(transaction, store, createID(item.id.client, item.id.clock + stackItem.len))
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          struct = item
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        if (!struct.deleted && isParentOf(type, /** @type {Item} */ (struct))) {
 | 
					        if (!struct.deleted && isParentOf(type, /** @type {Item} */ (struct))) {
 | 
				
			||||||
          struct.delete(transaction)
 | 
					          struct.delete(transaction)
 | 
				
			||||||
          performedChange = true
 | 
					          performedChange = true
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      })
 | 
					      })
 | 
				
			||||||
      result = stackItem
 | 
					      result = stackItem
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }, undoManager)
 | 
					 | 
				
			||||||
      if (result != null) {
 | 
					      if (result != null) {
 | 
				
			||||||
        undoManager.emit('stack-item-popped', [{ stackItem: result, type: eventType }, undoManager])
 | 
					        undoManager.emit('stack-item-popped', [{ stackItem: result, type: eventType }, undoManager])
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }, undoManager)
 | 
				
			||||||
  return result
 | 
					  return result
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user