diff --git a/src/utils/YEvent.js b/src/utils/YEvent.js index 0b615c1d..d5774ae1 100644 --- a/src/utils/YEvent.js +++ b/src/utils/YEvent.js @@ -8,6 +8,7 @@ import { import * as set from 'lib0/set' import * as array from 'lib0/array' import { addsStruct } from './Transaction.js' +import { ListCursor } from './ListCursor.js' /** * YEvent describes the changes on a YType. @@ -62,7 +63,7 @@ export class YEvent { */ get path () { // @ts-ignore _item is defined because target is integrated - return getPathTo(this.currentTarget, this.target) + return getPathTo(this.currentTarget, this.target, this.transaction) } /** @@ -297,12 +298,13 @@ export class YEvent { * * @param {AbstractType} parent * @param {AbstractType} child target + * @param {Transaction} tr * @return {Array} Path to the target * * @private * @function */ -const getPathTo = (parent, child) => { +const getPathTo = (parent, child, tr) => { const path = [] while (child._item !== null && child !== parent) { if (child._item.parentSub !== null) { @@ -310,15 +312,11 @@ const getPathTo = (parent, child) => { path.unshift(child._item.parentSub) } else { // parent is array-ish - let i = 0 - let c = /** @type {AbstractType} */ (child._item.parent)._start - while (c !== child._item && c !== null) { - if (!c.deleted) { - i++ - } - c = c.right + const c = new ListCursor(/** @type {AbstractType} */ (child._item.parent)) + while (c.nextItem != null && !c.reachedEnd && c.nextItem !== child._item) { + c.forward(tr, (c.nextItem.countable && !c.nextItem.deleted) ? c.nextItem.length : 0, true) } - path.unshift(i) + path.unshift(c.index) } child = /** @type {AbstractType} */ (child._item.parent) } diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index b1f619bd..84814239 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -1,10 +1,56 @@ -import { init, compare, applyRandomTests, Doc, AbstractType, TestConnector, Item } from './testHelper.js' // eslint-disable-line +import { init, compare, applyRandomTests, Doc, Item } from './testHelper.js' // eslint-disable-line import * as Y from '../src/index.js' import * as t from 'lib0/testing' import * as prng from 'lib0/prng' import * as math from 'lib0/math' +/** + * path should be correct when moving item - see yjs#481 + * + * @param {t.TestCase} tc + */ +export const testArrayMovePathIssue481 = tc => { + const { users, testConnector, array0, array1 } = init(tc, { users: 2 }) + array0.observeDeep(events => { + events.forEach(event => { + if (event.path.length > 0) { + /** + * @type {any} + */ + let target = event.currentTarget + event.path.forEach(p => { + target = target.get(p) + }) + t.assert(target === event.target) + } + }) + }) + array0.push([ + ['a', '1.1'], + ['b', '2.2'], + ['c', '3.1'], + ['d', '4.1'], + ['e', '5.1'] + ].map(e => Y.Array.from(e))) + testConnector.flushAllMessages() + users[1].transact(() => { + array1.get(1).insert(0, ['0']) + array1.move(1, 0) + }) + testConnector.flushAllMessages() + users[1].transact(() => { + array1.get(3).insert(0, ['1']) + array1.move(3, 4) + }) + testConnector.flushAllMessages() + users[1].transact(() => { + array1.get(2).insert(0, ['2']) + array1.move(2, array1.length) + }) + testConnector.flushAllMessages() +} + /** * foreach has correct index - see yjs#485 *