From 5f4c51477e8ef448febadc3204e538c7387f1f96 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 18 Sep 2015 09:32:36 +0200 Subject: [PATCH] IDEA-144655 Smart PSI pointers should retain old offsets after inserting text after them --- .../SmartPsiElementPointersTest.java | 19 +++++++++++ .../psi/impl/smartPointers/MarkerCache.java | 34 ++++++++++++++++--- .../SmartPointerManagerImpl.java | 4 +-- .../SmartPsiElementPointerImpl.java | 25 +++++++------- 4 files changed, 63 insertions(+), 19 deletions(-) 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 21883175fbf6..11c5c142f365 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 @@ -24,6 +24,7 @@ import com.intellij.lang.FileASTNode; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.EditorFactory; +import com.intellij.openapi.editor.EditorModificationUtil; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.event.DocumentListener; import com.intellij.openapi.editor.event.EditorEventMulticaster; @@ -754,6 +755,24 @@ public class SmartPsiElementPointersTest extends CodeInsightTestCase { PsiDocumentManager.getInstance(myProject).commitAllDocuments(); assertEquals(TextRange.create(2, 3), pointer.getRange()); } + + public void testUpdateAfterInsertingIdenticalText() { + PsiJavaFile file = (PsiJavaFile)configureByText(StdFileTypes.JAVA, "class Foo {\n" + + " void m() {\n" + + " }\n" + + "}\n"); + PsiMethod method = file.getClasses()[0].getMethods()[0]; + TextRange originalRange = method.getTextRange(); + SmartPsiElementPointer pointer = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(method); + + EditorModificationUtil.insertStringAtCaret(myEditor, " void m() {\n" + + " }\n"); + PsiDocumentManager.getInstance(myProject).commitDocument(myEditor.getDocument()); + PsiElement element = pointer.getElement(); + assertNotNull(element); + TextRange newRange = element.getTextRange(); + assertEquals(originalRange, newRange); + } public void testAnchorInfoSurvivesPsiChange() { PsiJavaFile file = (PsiJavaFile)configureByText(JavaFileType.INSTANCE, "class C1{}\nclass C2 {}"); 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 9c6cfeec8771..f3f481eec90f 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 @@ -23,6 +23,7 @@ import com.intellij.openapi.editor.impl.event.RetargetRangeMarkers; import com.intellij.openapi.util.ProperTextRange; import com.intellij.openapi.util.Trinity; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiElement; import com.intellij.util.NullableFunction; import com.intellij.util.containers.ContainerUtil; import gnu.trove.TLongObjectHashMap; @@ -113,17 +114,40 @@ class MarkerCache { synchronized void updateMarkers(@NotNull FrozenDocument frozen, @NotNull List events) { TLongObjectHashMap updated = getUpdatedMarkers(frozen, events); - for (SelfElementInfo info : getInfos()) { - long key = info.markerCacheKey(); - if (key != 0) { - ManualRangeMarker newRange = updated.get(key); - info.setRange(newRange == null ? null : newRange.getRange()); + for (SmartPsiElementPointerImpl pointer : myPointers.getAlivePointers()) { + SmartPointerElementInfo info = pointer.getElementInfo(); + if (info instanceof SelfElementInfo) { + long key = ((SelfElementInfo)info).markerCacheKey(); + if (key != 0) { + ManualRangeMarker newRangeMarker = updated.get(key); + ProperTextRange newRange = newRangeMarker == null ? null : newRangeMarker.getRange(); + ((SelfElementInfo)info).setRange(newRange); + + if (newRange != null && !(pointer instanceof SmartPsiFileRangePointerImpl)) { + updatePointerTarget(pointer, newRange); + } + } + } } myUpdatedRanges = null; } + // after reparse and its complex tree diff, the element might have "moved" to other range + // but if an element of the same type can still be found at the old range, let's point there + private static void updatePointerTarget(@NotNull SmartPsiElementPointerImpl pointer, @NotNull ProperTextRange newRange) { + E cachedElement = pointer.getCachedElement(); + if (cachedElement == null || cachedElement.isValid() && newRange.equals(cachedElement.getTextRange())) { + return; + } + + E newTarget = pointer.doRestoreElement(); + if (newTarget != null) { + pointer.cacheElement(newTarget); + } + } + @NotNull private List getInfos() { return ContainerUtil.findAll(ContainerUtil.map(myPointers.getAlivePointers(), new NullableFunction() { diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java index fd4cb32eeab3..306ef9b8d6e6 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java @@ -134,8 +134,8 @@ public class SmartPointerManagerImpl extends SmartPointerManager { Reference data = element.getUserData(CACHED_SMART_POINTER_KEY); SmartPointerEx cachedPointer = SoftReference.dereference(data); if (cachedPointer != null) { - PsiElement cachedElement = cachedPointer.getCachedElement(); - if (cachedElement != null && cachedElement != element) { + PsiElement cachedElement = cachedPointer.getElement(); + if (cachedElement == null || cachedElement != element) { return null; } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java index 0ccc5fd5deac..e19b364f3cc3 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java @@ -80,23 +80,24 @@ class SmartPsiElementPointerImpl implements SmartPointerEx @Nullable public E getElement() { E element = getCachedElement(); - if (element != null && !element.isValid()) { - element = null; - } - if (element == null) { - //noinspection unchecked - element = (E)myElementInfo.restoreElement(); - if (element != null && (!element.getClass().equals(myElementClass) || !element.isValid())) { - element = null; - } - + if (element == null || !element.isValid()) { + element = doRestoreElement(); cacheElement(element); } - return element; } - private void cacheElement(E element) { + @Nullable + E doRestoreElement() { + //noinspection unchecked + E element = (E)myElementInfo.restoreElement(); + if (element != null && (!element.getClass().equals(myElementClass) || !element.isValid())) { + return null; + } + return element; + } + + void cacheElement(@Nullable E element) { myElement = element == null ? null : ((PsiManagerEx)PsiManager.getInstance(getProject())).isBatchFilesProcessingMode() ? new WeakReference(element) : new SoftReference(element);