diff --git a/java/java-impl/src/com/intellij/psi/impl/smartPointers/JavaAnchorProvider.java b/java/java-impl/src/com/intellij/psi/impl/smartPointers/JavaAnchorProvider.java index 21499dd45d74..df0c12ab5c74 100644 --- a/java/java-impl/src/com/intellij/psi/impl/smartPointers/JavaAnchorProvider.java +++ b/java/java-impl/src/com/intellij/psi/impl/smartPointers/JavaAnchorProvider.java @@ -30,16 +30,14 @@ public class JavaAnchorProvider extends SmartPointerAnchorProvider { return null; } - if (element instanceof PsiClass) { - if (element instanceof PsiAnonymousClass) { - return ((PsiAnonymousClass)element).getBaseClassReference().getReferenceNameElement(); - } else { - return ((PsiClass)element).getNameIdentifier(); - } - } else if (element instanceof PsiMethod) { - return ((PsiMethod)element).getNameIdentifier(); - } else if (element instanceof PsiVariable) { - return ((PsiVariable)element).getNameIdentifier(); + if (element instanceof PsiAnonymousClass) { + return ((PsiAnonymousClass)element).getBaseClassReference().getReferenceNameElement(); + } + if (element instanceof PsiClass || element instanceof PsiMethod || element instanceof PsiVariable) { + return ((PsiNameIdentifierOwner)element).getNameIdentifier(); + } + if (element instanceof PsiImportList) { + return element.getContainingFile(); } return null; } @@ -55,6 +53,9 @@ public class JavaAnchorProvider extends SmartPointerAnchorProvider { return parent; } + if (anchor instanceof PsiJavaFile) { + return ((PsiJavaFile)anchor).getImportList(); + } return null; } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy index 7672c85417f7..16bfc4396dd6 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy @@ -996,7 +996,7 @@ class Foo { assertTopLevelFoldRegionsState "[FoldRegion +(49:92), placeholder='otherMethod() → { ', FoldRegion +(113:123), placeholder=' }']" } - public void "disabled - test imports remain collapsed when new item is added at the end"() { + public void "test imports remain collapsed when new item is added at the end"() { boolean oldValue = CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY = true DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true); // tests disable this by default diff --git a/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java b/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java index 72d1fefdcf9c..81d3a7201a47 100644 --- a/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java @@ -931,4 +931,26 @@ public class SmartPsiElementPointersTest extends CodeInsightTestCase { assertFalse(createPointer(clazz).hashCode() == createPointer(clazz.getMethods()[0]).hashCode()); } + public void testImportListPointerSurvivesImportAddition() throws Exception { + PsiJavaFile file = (PsiJavaFile)createFile("a.java", "import foo.Bar;\nclass Foo {}"); + SmartPointerEx pointer = createPointer(file.getImportList()); + Document document = file.getViewProvider().getDocument(); + + WriteCommandAction.runWriteCommandAction(myProject, () -> { + document.insertString(document.getText().indexOf("class"), "import foo.Goo;\n"); + PsiDocumentManager.getInstance(myProject).commitAllDocuments(); + }); + assertEquals(file.getImportList(), pointer.getElement()); + assertSize(2, file.getImportList().getImportStatements()); + + WriteCommandAction.runWriteCommandAction(myProject, () -> { + document.insertString(0, " "); + PsiDocumentManager.getInstance(myProject).commitAllDocuments(); + document.deleteString(0, document.getText().indexOf("\nimport")); + PsiDocumentManager.getInstance(myProject).commitAllDocuments(); + }); + assertEquals(file.getImportList(), pointer.getElement()); + assertSize(1, file.getImportList().getImportStatements()); + } + } diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java index 6ff8ae16e48f..afc67e44b495 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java @@ -36,7 +36,6 @@ class AnchorElementInfo extends SelfElementInfo { AnchorElementInfo(@NotNull PsiElement anchor, @NotNull PsiFile containingFile, Identikit.ByAnchor identikit) { super(containingFile.getProject(), ProperTextRange.create(anchor.getTextRange()), identikit, containingFile, false); - assert !(anchor instanceof PsiFile) : "FileElementInfo must be used for file: "+anchor; myStubElementTypeAndId = pack(-1, null); } // will restore by stub index until file tree get loaded diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/Identikit.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/Identikit.java index 17d934a81cdd..a2ff3912a450 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/Identikit.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/Identikit.java @@ -22,6 +22,7 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.IFileElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.containers.WeakInterner; @@ -41,6 +42,8 @@ public abstract class Identikit { @NotNull public abstract Language getFileLanguage(); + public abstract boolean isForPsiFile(); + public static ByType fromPsi(@NotNull PsiElement element, @NotNull Language fileLanguage) { return fromTypes(element.getClass(), PsiUtilCore.getElementType(element), fileLanguage); } @@ -156,6 +159,11 @@ public abstract class Identikit { return myFileLanguage; } + @Override + public boolean isForPsiFile() { + return myElementType instanceof IFileElementType; + } + private boolean isAcceptable(@NotNull PsiElement element) { return myElementClass == element.getClass() && myElementType == PsiUtilCore.getElementType(element); } @@ -204,6 +212,11 @@ public abstract class Identikit { public Language getFileLanguage() { return myAnchorInfo.getFileLanguage(); } + + @Override + public boolean isForPsiFile() { + return myAnchorInfo.isForPsiFile(); + } } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/MarkerCache.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/MarkerCache.java index 05bc1cf7261e..de4f05ff1b50 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/MarkerCache.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/MarkerCache.java @@ -45,7 +45,7 @@ class MarkerCache { o2 = info2.getPsiEndOffset(); if (o1 != o2) return o1 > o2 ? 1 : -1; - return (info1.isForInjected() ? 1 : 0) - (info2.isForInjected() ? 1 : 0); + return (info1.isGreedy() ? 1 : 0) - (info2.isGreedy() ? 1 : 0); } }; private final SmartPointerTracker myPointers; @@ -83,13 +83,13 @@ class MarkerCache { int i = 0; while (i < markers.length) { SelfElementInfo info = infos.get(i); - boolean forInjected = info.isForInjected(); + boolean greedy = info.isGreedy(); int start = info.getPsiStartOffset(); int end = info.getPsiEndOffset(); - markers[i] = new ManualRangeMarker(start, end, forInjected, forInjected, !forInjected, null); + markers[i] = new ManualRangeMarker(start, end, greedy, greedy, !greedy, null); i++; - while (i < markers.length && rangeEquals(infos.get(i), start, end, forInjected)) { + while (i < markers.length && rangeEquals(infos.get(i), start, end, greedy)) { markers[i] = markers[i - 1]; i++; } @@ -97,8 +97,8 @@ class MarkerCache { return markers; } - private static boolean rangeEquals(SelfElementInfo info, int start, int end, boolean injected) { - return start == info.getPsiStartOffset() && end == info.getPsiEndOffset() && injected == info.isForInjected(); + private static boolean rangeEquals(SelfElementInfo info, int start, int end, boolean greedy) { + return start == info.getPsiStartOffset() && end == info.getPsiEndOffset() && greedy == info.isGreedy(); } private static UpdatedRanges applyEvents(@NotNull List events, final UpdatedRanges struct) { diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java index 82ee5eb1710d..c8a92f8fc58a 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java @@ -89,8 +89,8 @@ public class SelfElementInfo extends SmartPointerElementInfo { return myEndOffset; } - boolean isForInjected() { - return myForInjected; + boolean isGreedy() { + return myForInjected || myIdentikit.isForPsiFile(); } @Override