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,19 +222,22 @@ 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
|
|
||||||
let n = right.left
|
|
||||||
while (n !== null) {
|
|
||||||
if (!n.deleted && n.countable) {
|
|
||||||
index += n.length
|
|
||||||
}
|
|
||||||
n = n.left
|
|
||||||
}
|
|
||||||
type = right.parent
|
type = right.parent
|
||||||
|
if (type._item !== null && !type._item.deleted) {
|
||||||
|
index = right.deleted || !right.countable ? 0 : res.diff
|
||||||
|
let n = right.left
|
||||||
|
while (n !== null) {
|
||||||
|
if (!n.deleted && n.countable) {
|
||||||
|
index += n.length
|
||||||
|
}
|
||||||
|
n = n.left
|
||||||
|
}
|
||||||
|
}
|
||||||
} 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
|
||||||
|
if (result != null) {
|
||||||
|
undoManager.emit('stack-item-popped', [{ stackItem: result, type: eventType }, undoManager])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, undoManager)
|
}, undoManager)
|
||||||
if (result != null) {
|
|
||||||
undoManager.emit('stack-item-popped', [{ stackItem: result, type: eventType }, undoManager])
|
|
||||||
}
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user