From fe06edef15e31dc2a554b5b2a5af64db03790c7d Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 4 Jan 2018 22:49:23 +0100 Subject: [PATCH] correct event firing when AST has no PSI (EA-111703 - IAE: PsiChangeHandler.$$$reportNull$$$) --- .../pom/tree/events/impl/ChangeInfoImpl.java | 24 ++++++++++++++----- .../pom/tree/events/impl/TreeChangeImpl.java | 9 ++++++- .../psi/impl/PsiEventConsistencyTest.kt | 18 ++++++++++++++ 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/platform/core-impl/src/com/intellij/pom/tree/events/impl/ChangeInfoImpl.java b/platform/core-impl/src/com/intellij/pom/tree/events/impl/ChangeInfoImpl.java index 4d28e2d07820..8141291a0382 100644 --- a/platform/core-impl/src/com/intellij/pom/tree/events/impl/ChangeInfoImpl.java +++ b/platform/core-impl/src/com/intellij/pom/tree/events/impl/ChangeInfoImpl.java @@ -22,6 +22,7 @@ import com.intellij.psi.impl.PsiManagerImpl; import com.intellij.psi.impl.PsiTreeChangeEventImpl; import com.intellij.psi.impl.source.tree.CompositeElement; import com.intellij.psi.impl.source.tree.TreeElement; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class ChangeInfoImpl implements ChangeInfo { @@ -64,12 +65,10 @@ public class ChangeInfoImpl implements ChangeInfo { } void fireEvent(int parentStart, PsiFile file, CompositeElement parent) { - PsiTreeChangeEventImpl e = new PsiTreeChangeEventImpl(file.getManager()); - e.setFile(file); - e.setOffset(myOffset + parentStart); + PsiTreeChangeEventImpl e = createEvent(file, myOffset + parentStart); if (myOldChild == myNewChild && myNewChild != null) { - childrenChanged(e, myNewChild); + childrenChanged(e, myNewChild, myOldLength); } else if (myOldChild != null && myNewChild != null) { childReplaced(e, myOldChild, myNewChild, parent); @@ -82,6 +81,19 @@ public class ChangeInfoImpl implements ChangeInfo { } } + @NotNull + static PsiTreeChangeEventImpl createEvent(PsiFile file, int offset) { + PsiTreeChangeEventImpl e = new PsiTreeChangeEventImpl(file.getManager()); + e.setFile(file); + e.setOffset(offset); + return e; + } + + boolean hasNoPsi() { + return myOldChild != null && myOldChild.getPsi() == null || + myNewChild != null && myNewChild.getPsi() == null; + } + private static void childAdded(PsiTreeChangeEventImpl e, TreeElement child, CompositeElement parent) { e.setParent(parent.getPsi()); e.setChild(child.getPsi()); @@ -104,9 +116,9 @@ public class ChangeInfoImpl implements ChangeInfo { getPsiManagerImpl(e).childReplaced(e); } - private void childrenChanged(PsiTreeChangeEventImpl e, TreeElement parent) { + static void childrenChanged(PsiTreeChangeEventImpl e, TreeElement parent, int oldLength) { e.setParent(parent.getPsi()); - e.setOldLength(myOldLength); + e.setOldLength(oldLength); getPsiManagerImpl(e).childrenChanged(e); } 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 c35c8ef1e335..d65dc93da0c0 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 @@ -39,6 +39,7 @@ public class TreeChangeImpl implements TreeChange, Comparable { public TreeChangeImpl(@NotNull CompositeElement parent) { myParent = parent; + assert myParent.getPsi() != null; mySuperParents = JBIterable.generate(parent.getTreeParent(), TreeElement::getTreeParent).toList(); for (TreeElement child : getCurrentChildren()) { myInitialChildren.add(child); @@ -152,7 +153,13 @@ public class TreeChangeImpl implements TreeChange, Comparable { void fireEvents(PsiFile file) { int start = myParent.getStartOffset(); - for (ChangeInfoImpl change : getAllChanges().values()) { + Collection changes = getAllChanges().values(); + if (ContainerUtil.exists(changes, c -> c.hasNoPsi())) { + ChangeInfoImpl.childrenChanged(ChangeInfoImpl.createEvent(file, start), myParent, myParent.getTextLength() - getLengthDelta()); + return; + } + + for (ChangeInfoImpl change : changes) { change.fireEvent(start, file, myParent); } } 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 5869f8016154..ea15a7dbd4d5 100644 --- a/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiEventConsistencyTest.kt +++ b/platform/platform-tests/testSrc/com/intellij/psi/impl/PsiEventConsistencyTest.kt @@ -125,6 +125,17 @@ class PsiEventConsistencyTest : LightPlatformCodeInsightFixtureTestCase() { assertEquals(root.text, file.viewProvider.document!!.text) } } + + fun `test changes on AST without PSI`() { + val file = createEmptyFile() + WriteCommandAction.runWriteCommandAction(project) { + val root = file.node as FileElement + root.replaceChild(root.firstChildNode, leaf(leafTypes[0], "A")) + + assertEquals("A", root.text) + assertEquals(root.text, file.viewProvider.document!!.text) + } + } fun testPsiDocSynchronization() { ImperativeCommand.checkScenarios { RandomAstChanges() } @@ -198,6 +209,13 @@ class PsiEventConsistencyTest : LightPlatformCodeInsightFixtureTestCase() { } private fun leaf(type: IElementType, text: String): TreeElement { + if (text[0].isUpperCase()) { + // no PSI + return withDummyHolder(object : LeafElement(type, text) { + override fun toString() = text + }) + } + return withDummyHolder(object : LeafPsiElement(type, text) { override fun toString() = text })