[UndoManager] stop tracking unrelated insertions - yjs/y-monaco#10
This commit is contained in:
parent
d812636c5b
commit
e0b76cd2f4
@ -614,8 +614,9 @@ parameter that is stored on <code>transaction.origin</code> and
|
|||||||
<b><code>toJSON():any</code></b>
|
<b><code>toJSON():any</code></b>
|
||||||
<dd>
|
<dd>
|
||||||
Deprecated: It is recommended to call toJSON directly on the shared types.
|
Deprecated: It is recommended to call toJSON directly on the shared types.
|
||||||
Converts the entire document into a js object, recursively traversing each yjs type. Doesn't
|
Converts the entire document into a js object, recursively traversing each yjs
|
||||||
log types that have not been defined (using <code>ydoc.getType(..)</code>).
|
type. Doesn't log types that have not been defined (using
|
||||||
|
<code>ydoc.getType(..)</code>).
|
||||||
</dd>
|
</dd>
|
||||||
<b><code>get(string, Y.[TypeClass]):[Type]</code></b>
|
<b><code>get(string, Y.[TypeClass]):[Type]</code></b>
|
||||||
<dd>Define a shared type.</dd>
|
<dd>Define a shared type.</dd>
|
||||||
|
@ -5,11 +5,11 @@ import {
|
|||||||
transact,
|
transact,
|
||||||
createID,
|
createID,
|
||||||
redoItem,
|
redoItem,
|
||||||
iterateStructs,
|
|
||||||
isParentOf,
|
isParentOf,
|
||||||
followRedone,
|
followRedone,
|
||||||
getItemCleanStart,
|
getItemCleanStart,
|
||||||
getState,
|
isDeleted,
|
||||||
|
addToDeleteSet,
|
||||||
Transaction, Doc, Item, GC, DeleteSet, AbstractType, YEvent // eslint-disable-line
|
Transaction, Doc, Item, GC, DeleteSet, AbstractType, YEvent // eslint-disable-line
|
||||||
} from '../internals.js'
|
} from '../internals.js'
|
||||||
|
|
||||||
@ -18,14 +18,12 @@ import { Observable } from 'lib0/observable.js'
|
|||||||
|
|
||||||
class StackItem {
|
class StackItem {
|
||||||
/**
|
/**
|
||||||
* @param {DeleteSet} ds
|
* @param {DeleteSet} deletions
|
||||||
* @param {Map<number,number>} beforeState
|
* @param {DeleteSet} insertions
|
||||||
* @param {Map<number,number>} afterState
|
|
||||||
*/
|
*/
|
||||||
constructor (ds, beforeState, afterState) {
|
constructor (deletions, insertions) {
|
||||||
this.ds = ds
|
this.insertions = insertions
|
||||||
this.beforeState = beforeState
|
this.deletions = deletions
|
||||||
this.afterState = afterState
|
|
||||||
/**
|
/**
|
||||||
* Use this to save and restore metadata like selection range
|
* Use this to save and restore metadata like selection range
|
||||||
*/
|
*/
|
||||||
@ -65,48 +63,26 @@ const popStackItem = (undoManager, stack, eventType) => {
|
|||||||
*/
|
*/
|
||||||
const itemsToDelete = []
|
const itemsToDelete = []
|
||||||
let performedChange = false
|
let performedChange = false
|
||||||
stackItem.afterState.forEach((endClock, client) => {
|
iterateDeletedStructs(transaction, stackItem.insertions, struct => {
|
||||||
const startClock = stackItem.beforeState.get(client) || 0
|
if (struct instanceof Item) {
|
||||||
const len = endClock - startClock
|
if (struct.redone !== null) {
|
||||||
// @todo iterateStructs should not need the structs parameter
|
let { item, diff } = followRedone(store, struct.id)
|
||||||
const structs = /** @type {Array<GC|Item>} */ (store.clients.get(client))
|
if (diff > 0) {
|
||||||
if (startClock !== endClock) {
|
item = getItemCleanStart(transaction, createID(item.id.client, item.id.clock + diff))
|
||||||
// make sure structs don't overlap with the range of created operations [stackItem.start, stackItem.start + stackItem.end)
|
|
||||||
// this must be executed before deleted structs are iterated.
|
|
||||||
getItemCleanStart(transaction, createID(client, startClock))
|
|
||||||
if (endClock < getState(doc.store, client)) {
|
|
||||||
getItemCleanStart(transaction, createID(client, endClock))
|
|
||||||
}
|
|
||||||
iterateStructs(transaction, structs, startClock, len, struct => {
|
|
||||||
if (struct instanceof Item) {
|
|
||||||
if (struct.redone !== null) {
|
|
||||||
let { item, diff } = followRedone(store, struct.id)
|
|
||||||
if (diff > 0) {
|
|
||||||
item = getItemCleanStart(transaction, createID(item.id.client, item.id.clock + diff))
|
|
||||||
}
|
|
||||||
if (item.length > len) {
|
|
||||||
getItemCleanStart(transaction, createID(item.id.client, endClock))
|
|
||||||
}
|
|
||||||
struct = item
|
|
||||||
}
|
|
||||||
if (!struct.deleted && scope.some(type => isParentOf(type, /** @type {Item} */ (struct)))) {
|
|
||||||
itemsToDelete.push(struct)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
struct = item
|
||||||
|
}
|
||||||
|
if (!struct.deleted && scope.some(type => isParentOf(type, /** @type {Item} */ (struct)))) {
|
||||||
|
itemsToDelete.push(struct)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
iterateDeletedStructs(transaction, stackItem.ds, struct => {
|
iterateDeletedStructs(transaction, stackItem.deletions, struct => {
|
||||||
const id = struct.id
|
|
||||||
const clock = id.clock
|
|
||||||
const client = id.client
|
|
||||||
const startClock = stackItem.beforeState.get(client) || 0
|
|
||||||
const endClock = stackItem.afterState.get(client) || 0
|
|
||||||
if (
|
if (
|
||||||
struct instanceof Item &&
|
struct instanceof Item &&
|
||||||
scope.some(type => isParentOf(type, struct)) &&
|
scope.some(type => isParentOf(type, struct)) &&
|
||||||
// Never redo structs in [stackItem.start, stackItem.start + stackItem.end) because they were created and deleted in the same capture interval.
|
// Never redo structs in stackItem.insertions because they were created and deleted in the same capture interval.
|
||||||
!(clock >= startClock && clock < endClock)
|
!isDeleted(stackItem.insertions, struct.id)
|
||||||
) {
|
) {
|
||||||
itemsToRedo.add(struct)
|
itemsToRedo.add(struct)
|
||||||
}
|
}
|
||||||
@ -201,17 +177,23 @@ export class UndoManager extends Observable {
|
|||||||
// neither undoing nor redoing: delete redoStack
|
// neither undoing nor redoing: delete redoStack
|
||||||
this.redoStack = []
|
this.redoStack = []
|
||||||
}
|
}
|
||||||
const beforeState = transaction.beforeState
|
const insertions = new DeleteSet()
|
||||||
const afterState = transaction.afterState
|
transaction.afterState.forEach((endClock, client) => {
|
||||||
|
const startClock = transaction.beforeState.get(client) || 0
|
||||||
|
const len = endClock - startClock
|
||||||
|
if (len > 0) {
|
||||||
|
addToDeleteSet(insertions, client, startClock, len)
|
||||||
|
}
|
||||||
|
})
|
||||||
const now = time.getUnixTime()
|
const now = time.getUnixTime()
|
||||||
if (now - this.lastChange < captureTimeout && stack.length > 0 && !undoing && !redoing) {
|
if (now - this.lastChange < captureTimeout && stack.length > 0 && !undoing && !redoing) {
|
||||||
// append change to last stack op
|
// append change to last stack op
|
||||||
const lastOp = stack[stack.length - 1]
|
const lastOp = stack[stack.length - 1]
|
||||||
lastOp.ds = mergeDeleteSets([lastOp.ds, transaction.deleteSet])
|
lastOp.deletions = mergeDeleteSets([lastOp.deletions, transaction.deleteSet])
|
||||||
lastOp.afterState = afterState
|
lastOp.insertions = mergeDeleteSets([lastOp.insertions, insertions])
|
||||||
} else {
|
} else {
|
||||||
// create a new stack op
|
// create a new stack op
|
||||||
stack.push(new StackItem(transaction.deleteSet, beforeState, afterState))
|
stack.push(new StackItem(transaction.deleteSet, insertions))
|
||||||
}
|
}
|
||||||
if (!undoing && !redoing) {
|
if (!undoing && !redoing) {
|
||||||
this.lastChange = now
|
this.lastChange = now
|
||||||
@ -232,7 +214,7 @@ export class UndoManager extends Observable {
|
|||||||
* @param {StackItem} stackItem
|
* @param {StackItem} stackItem
|
||||||
*/
|
*/
|
||||||
const clearItem = stackItem => {
|
const clearItem = stackItem => {
|
||||||
iterateDeletedStructs(transaction, stackItem.ds, item => {
|
iterateDeletedStructs(transaction, stackItem.deletions, item => {
|
||||||
if (item instanceof Item && this.scope.some(type => isParentOf(type, item))) {
|
if (item instanceof Item && this.scope.some(type => isParentOf(type, item))) {
|
||||||
keepItem(item, false)
|
keepItem(item, false)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user