diff --git a/platform/core-impl/src/com/intellij/pom/tree/events/impl/TreeChangeEventImpl.java b/platform/core-impl/src/com/intellij/pom/tree/events/impl/TreeChangeEventImpl.java index 75fabb814444..85d16fe5ebd8 100644 --- a/platform/core-impl/src/com/intellij/pom/tree/events/impl/TreeChangeEventImpl.java +++ b/platform/core-impl/src/com/intellij/pom/tree/events/impl/TreeChangeEventImpl.java @@ -145,8 +145,7 @@ public class TreeChangeEventImpl implements TreeChangeEvent{ } public void fireEvents() { - Collection changes = ContainerUtil.sorted(myChangedElements.values(), - Comparator.comparing(TreeChangeImpl::getInitialStart)); + Collection changes = ContainerUtil.sorted(myChangedElements.values()); for (TreeChangeImpl change : changes) { change.fireEvents((PsiFile)myFileElement.getPsi()); } diff --git a/platform/core-impl/src/com/intellij/pom/tree/events/impl/TreeChangeImpl.java b/platform/core-impl/src/com/intellij/pom/tree/events/impl/TreeChangeImpl.java index f3e651a4cd4f..38de8aee42d9 100644 --- a/platform/core-impl/src/com/intellij/pom/tree/events/impl/TreeChangeImpl.java +++ b/platform/core-impl/src/com/intellij/pom/tree/events/impl/TreeChangeImpl.java @@ -22,24 +22,23 @@ import com.intellij.pom.tree.events.TreeChange; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.source.tree.CompositeElement; import com.intellij.psi.impl.source.tree.TreeElement; +import com.intellij.util.ArrayUtil; import com.intellij.util.containers.JBIterable; import org.jetbrains.annotations.NotNull; import java.util.*; -public class TreeChangeImpl implements TreeChange { +public class TreeChangeImpl implements TreeChange, Comparable { private final CompositeElement myParent; private final List mySuperParents; private final LinkedHashSet myInitialChildren = new LinkedHashSet<>(); private final Map myInitialLengths = new HashMap<>(); private final Set myContentChangeChildren = new HashSet<>(); - private final int myInitialStart; private Map myChanges; public TreeChangeImpl(@NotNull CompositeElement parent) { myParent = parent; mySuperParents = JBIterable.generate(parent.getTreeParent(), TreeElement::getTreeParent).toList(); - myInitialStart = myParent.getStartOffset(); for (TreeElement child : getCurrentChildren()) { myInitialChildren.add(child); myInitialLengths.put(child, child.getTextLength()); @@ -55,8 +54,29 @@ public class TreeChangeImpl implements TreeChange { return JBIterable.generate(myParent.getFirstChildNode(), TreeElement::getTreeNext); } - int getInitialStart() { - return myInitialStart; + @Override + public int compareTo(@NotNull TreeChangeImpl o) { + List thisParents = getSuperParents(); + List thatParents = o.getSuperParents(); + for (int i = 1; i <= thisParents.size() && i <= thatParents.size(); i++) { + CompositeElement thisParent = i < thisParents.size() ? thisParents.get(thisParents.size() - i) : myParent; + CompositeElement thatParent = i < thatParents.size() ? thatParents.get(thatParents.size() - i) : o.myParent; + int result = compareNodePositions(thisParent, thatParent); + if (result != 0) return result; + } + return 0; + } + + private static int compareNodePositions(CompositeElement node1, CompositeElement node2) { + if (node1 == node2) return 0; + + int o1 = node1.getStartOffsetInParent(); + int o2 = node2.getStartOffsetInParent(); + return o1 != o2 ? Integer.compare(o1, o2) : Integer.compare(getChildIndex(node1), getChildIndex(node2)); + } + + private static int getChildIndex(CompositeElement e) { + return ArrayUtil.indexOf(e.getTreeParent().getChildren(null), e); } int getLengthDelta() { diff --git a/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiEventConsistencyTest.kt b/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiEventConsistencyTest.kt index e253fda6ebf3..38dd4c94a8cd 100644 --- a/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiEventConsistencyTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiEventConsistencyTest.kt @@ -78,6 +78,31 @@ class PsiEventConsistencyTest : LightPlatformCodeInsightFixtureTestCase() { assertEquals(root.text, file.viewProvider.document!!.text) } } + + fun `test changes in different AST parents at the same initial offset`() { + val file = createEmptyFile() + WriteCommandAction.runWriteCommandAction(project) { + //prepare + val root = file.node as FileElement + root.replaceChild(root.firstChildNode, composite(compositeTypes[0], + composite(compositeTypes[0]), + composite(compositeTypes[0], leaf(leafTypes[0], "x")), + composite(compositeTypes[0], leaf(leafTypes[0], "s")))) + + ChangeUtil.prepareAndRunChangeAction(ChangeUtil.ChangeAction { + val pComposite = root.firstChildNode.firstChildNode + val xComposite = pComposite.treeNext + val sComposite = xComposite.treeNext + sComposite.replaceChild(sComposite.firstChildNode, leaf(leafTypes[0], "h")) + ChangeUtil.prepareAndRunChangeAction(ChangeUtil.ChangeAction { + pComposite.addChild(leaf(leafTypes[0], "p")) + xComposite.removeChild(xComposite.firstChildNode) + }, root) + }, root) + assertEquals("ph", root.text) + assertEquals(root.text, file.viewProvider.document!!.text) + } + } fun testPsiDocSynchronization() { PropertyChecker.forAll(commands).shouldHold { cmd ->