smart pointers for different element types should have different hash codes clion/171.951
authorpeter <peter@jetbrains.com>
Mon, 14 Nov 2016 08:28:48 +0000 (09:28 +0100)
committerpeter <peter@jetbrains.com>
Mon, 14 Nov 2016 08:36:12 +0000 (09:36 +0100)
12 files changed:
java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsElementImpl.java
java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java
platform/core-impl/src/com/intellij/psi/PsiAnchor.java
platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java
platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfoFactory.java [deleted file]
platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorTypeInfo.java [deleted file]
platform/core-impl/src/com/intellij/psi/impl/smartPointers/Identikit.java [new file with mode: 0644]
platform/core-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java
platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java
platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java
platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiFileRangePointerImpl.java
platform/platform-resources/src/META-INF/LangExtensions.xml

index b9a5116983711859900fb131cd0e34fc92659c44..d6a7710fa78ebcaff42e9e441e6028a140af07dd 100644 (file)
@@ -27,8 +27,7 @@ import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.*;
 import com.intellij.psi.codeStyle.JavaCodeStyleSettingsFacade;
 import com.intellij.psi.impl.PsiElementBase;
-import com.intellij.psi.impl.smartPointers.AnchorTypeInfo;
-import com.intellij.psi.impl.smartPointers.SelfElementInfo;
+import com.intellij.psi.impl.smartPointers.Identikit;
 import com.intellij.psi.impl.source.SourceTreeToPsiMap;
 import com.intellij.psi.impl.source.tree.TreeElement;
 import com.intellij.psi.tree.IElementType;
@@ -47,7 +46,7 @@ public abstract class ClsElementImpl extends PsiElementBase implements PsiCompil
 
   private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.compiled.ClsElementImpl");
 
-  private volatile Pair<TextRange, AnchorTypeInfo> myMirror;
+  private volatile Pair<TextRange, Identikit.ByType> myMirror;
 
   @Override
   @NotNull
@@ -158,8 +157,8 @@ public abstract class ClsElementImpl extends PsiElementBase implements PsiCompil
   @Override
   public PsiElement getMirror() {
     PsiFile mirrorFile = ((ClsFileImpl)getContainingFile()).getMirror().getContainingFile();
-    Pair<TextRange, AnchorTypeInfo> mirror = myMirror;
-    return mirror == null ? null : SelfElementInfo.findElementInside(mirrorFile, mirror.first, mirror.second);
+    Pair<TextRange, Identikit.ByType> mirror = myMirror;
+    return mirror == null ? null : mirror.second.findPsiElement(mirrorFile, mirror.first.getStartOffset(), mirror.first.getEndOffset());
   }
 
   @Override
@@ -268,7 +267,7 @@ public abstract class ClsElementImpl extends PsiElementBase implements PsiCompil
 
     PsiElement psi = element.getPsi();
     psi.putUserData(COMPILED_ELEMENT, this);
-    myMirror = Pair.create(element.getTextRange(), AnchorTypeInfo.obtainInfo(psi, JavaLanguage.INSTANCE));
+    myMirror = Pair.create(element.getTextRange(), Identikit.fromPsi(psi, JavaLanguage.INSTANCE));
   }
 
   protected static <T extends  PsiElement> void setMirror(@Nullable T stub, @Nullable T mirror) throws InvalidMirrorException {
index 246dc65d43e7e3eb168a00892d0cafee144a72b4..72d1fefdcf9cfece267c34fec74402b83d9ff164 100644 (file)
@@ -926,4 +926,9 @@ public class SmartPsiElementPointersTest extends CodeInsightTestCase {
     }).cpuBound().assertTiming();
   }
 
+  public void testDifferentHashCodesForDifferentElementsInOneFile() throws Exception {
+    PsiClass clazz = ((PsiJavaFile)createFile("a.java", "class Foo { void foo(); }")).getClasses()[0];
+    assertFalse(createPointer(clazz).hashCode() == createPointer(clazz.getMethods()[0]).hashCode());
+  }
+
 }
index 3e3b6ca9bca16749ad65dbc931a676e8b9d71d77..e6f497ffa6b40ce9ce81500b2b7c6469e97aa514 100644 (file)
@@ -27,7 +27,7 @@ import com.intellij.openapi.util.Computable;
 import com.intellij.openapi.util.NullableComputable;
 import com.intellij.openapi.util.TextRange;
 import com.intellij.openapi.vfs.VirtualFile;
-import com.intellij.psi.impl.smartPointers.AnchorTypeInfo;
+import com.intellij.psi.impl.smartPointers.Identikit;
 import com.intellij.psi.impl.smartPointers.SelfElementInfo;
 import com.intellij.psi.impl.smartPointers.SmartPointerAnchorProvider;
 import com.intellij.psi.impl.source.PsiFileImpl;
@@ -111,7 +111,7 @@ public abstract class PsiAnchor {
       return wrapperOrHardReference(element);
     }
 
-    return new TreeRangeReference(file, textRange.getStartOffset(), textRange.getEndOffset(), AnchorTypeInfo.obtainInfo(element, lang), virtualFile);
+    return new TreeRangeReference(file, textRange.getStartOffset(), textRange.getEndOffset(), Identikit.fromPsi(element, lang), virtualFile);
   }
 
   @NotNull
@@ -178,14 +178,14 @@ public abstract class PsiAnchor {
   private static class TreeRangeReference extends PsiAnchor {
     private final VirtualFile myVirtualFile;
     private final Project myProject;
-    private final AnchorTypeInfo myInfo;
+    private final Identikit myInfo;
     private final int myStartOffset;
     private final int myEndOffset;
 
     private TreeRangeReference(@NotNull PsiFile file,
                                int startOffset,
                                int endOffset,
-                               @NotNull AnchorTypeInfo info,
+                               @NotNull Identikit info,
                                @NotNull VirtualFile virtualFile) {
       myVirtualFile = virtualFile;
       myProject = file.getProject();
@@ -200,7 +200,7 @@ public abstract class PsiAnchor {
       PsiFile psiFile = getFile();
       if (psiFile == null || !psiFile.isValid()) return null;
 
-      return SelfElementInfo.findElementInside(psiFile, myStartOffset, myEndOffset, myInfo);
+      return myInfo.findPsiElement(psiFile, myStartOffset, myEndOffset);
     }
 
     @Override
index db1f92bc4598e508e307bb2b5d2e5aca8f321bf9..6ff8ae16e48f0d75863ad2f3fd9b7603294b0bf9 100644 (file)
@@ -34,10 +34,8 @@ import org.jetbrains.annotations.Nullable;
 class AnchorElementInfo extends SelfElementInfo {
   private volatile long myStubElementTypeAndId; // stubId in the lower 32 bits; stubElementTypeIndex in the high 32 bits packed together for atomicity
 
-  AnchorElementInfo(@NotNull PsiElement anchor, @NotNull PsiFile containingFile) {
-    super(containingFile.getProject(), ProperTextRange.create(anchor.getTextRange()),
-          AnchorTypeInfo.obtainInfo(anchor, LanguageUtil.getRootLanguage(containingFile)),
-          containingFile, false);
+  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);
   }
@@ -47,7 +45,7 @@ class AnchorElementInfo extends SelfElementInfo {
                     int stubId,
                     @NotNull IStubElementType stubElementType) {
     super(containingFile.getProject(), null,
-          AnchorTypeInfo.obtainInfo(anchor.getClass(), stubElementType, LanguageUtil.getRootLanguage(containingFile)),
+          Identikit.fromTypes(anchor.getClass(), stubElementType, LanguageUtil.getRootLanguage(containingFile)),
           containingFile, false);
     myStubElementTypeAndId = pack(stubId, stubElementType);
     assert !(anchor instanceof PsiFile) : "FileElementInfo must be used for file: "+anchor;
@@ -76,27 +74,7 @@ class AnchorElementInfo extends SelfElementInfo {
       return PsiAnchor.restoreFromStubIndex((PsiFileWithStubSupport)file, stubId, stubElementType, false);
     }
 
-    Segment psiRange = getPsiRange();
-    if (psiRange == null) return null;
-
-    PsiFile file = restoreFile();
-    if (file == null) return null;
-    PsiElement anchor = findElementInside(file, psiRange, myType);
-    if (anchor == null) return null;
-
-    TextRange range = anchor.getTextRange();
-    if (range == null || range.getStartOffset() != psiRange.getStartOffset() || range.getEndOffset() != psiRange.getEndOffset()) return null;
-
-    return restoreFromAnchor(anchor);
-  }
-
-  @Nullable
-  static PsiElement restoreFromAnchor(PsiElement anchor) {
-    for (SmartPointerAnchorProvider provider : SmartPointerAnchorProvider.EP_NAME.getExtensions()) {
-      final PsiElement element = provider.restoreElement(anchor);
-      if (element != null) return element;
-    }
-    return anchor;
+    return super.restoreElement();
   }
 
   @Override
@@ -134,10 +112,7 @@ class AnchorElementInfo extends SelfElementInfo {
   }
 
   void switchToTreeRange(@NotNull PsiElement element) {
-    PsiElement anchor = AnchorElementInfoFactory.getAnchor(element);
-    if (anchor == null) anchor = element;
-    myType = AnchorTypeInfo.obtainInfo(anchor, myType.getFileLanguage());
-    setRange(anchor.getTextRange());
+    switchToAnchor(element);
     myStubElementTypeAndId = pack(-1, null);
   }
 
diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfoFactory.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfoFactory.java
deleted file mode 100644 (file)
index 0fc806b..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2000-2015 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.intellij.psi.impl.smartPointers;
-
-import com.intellij.psi.PsiAnchor;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiFile;
-import com.intellij.psi.StubBasedPsiElement;
-import com.intellij.psi.impl.source.PsiFileImpl;
-import com.intellij.psi.impl.source.PsiFileWithStubSupport;
-import com.intellij.psi.stubs.IStubElementType;
-import com.intellij.psi.stubs.StubTree;
-import com.intellij.psi.tree.IStubFileElementType;
-import com.intellij.psi.util.PsiUtilCore;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-public class AnchorElementInfoFactory {
-  @Nullable
-  public static SmartPointerElementInfo createElementInfo(@NotNull PsiElement element, @NotNull PsiFile containingFile) {
-    if (element instanceof StubBasedPsiElement && containingFile instanceof PsiFileWithStubSupport) {
-      PsiFileWithStubSupport stubFile = (PsiFileWithStubSupport)containingFile;
-      StubTree stubTree = stubFile.getStubTree();
-      if (stubTree != null) {
-        // use stubs when tree is not loaded
-        StubBasedPsiElement stubPsi = (StubBasedPsiElement)element;
-        int stubId = PsiAnchor.calcStubIndex(stubPsi);
-        IStubElementType myStubElementType = stubPsi.getElementType();
-
-        IStubFileElementType elementTypeForStubBuilder = ((PsiFileImpl)containingFile).getElementTypeForStubBuilder();
-        if (stubId != -1 && elementTypeForStubBuilder != null) { // TemplateDataElementType is not IStubFileElementType
-          return new AnchorElementInfo(element, stubFile, stubId, myStubElementType);
-        }
-      }
-    }
-
-    PsiElement anchor = getAnchor(element);
-    if (anchor != null) {
-      return new AnchorElementInfo(anchor, containingFile);
-    }
-    return null;
-  }
-
-  @Nullable
-  static PsiElement getAnchor(@NotNull PsiElement element) {
-    PsiUtilCore.ensureValid(element);
-    if (!element.isPhysical()) return null;
-
-    for (SmartPointerAnchorProvider provider : SmartPointerAnchorProvider.EP_NAME.getExtensions()) {
-      PsiElement anchor = provider.getAnchor(element);
-      if (anchor != null && anchor.isPhysical() && AnchorElementInfo.restoreFromAnchor(anchor) == element) {
-        return anchor;
-      }
-    }
-    return null;
-  }
-}
diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorTypeInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorTypeInfo.java
deleted file mode 100644 (file)
index 6702054..0000000
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2000-2016 JetBrains s.r.o.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.intellij.psi.impl.smartPointers;
-
-import com.google.common.base.MoreObjects;
-import com.intellij.lang.Language;
-import com.intellij.psi.PsiElement;
-import com.intellij.psi.tree.IElementType;
-import com.intellij.psi.util.PsiUtilCore;
-import com.intellij.util.containers.WeakInterner;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-/**
- * @author peter
- */
-public class AnchorTypeInfo {
-  private static final WeakInterner<AnchorTypeInfo> ourInterner = new WeakInterner<AnchorTypeInfo>();
-  private final Class myElementClass;
-  private final IElementType myElementType;
-  private final Language myFileLanguage;
-
-  private AnchorTypeInfo(Class elementClass, @Nullable IElementType elementType, Language fileLanguage) {
-    myElementClass = elementClass;
-    myElementType = elementType;
-    myFileLanguage = fileLanguage;
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) return true;
-    if (!(o instanceof AnchorTypeInfo)) return false;
-
-    AnchorTypeInfo info = (AnchorTypeInfo)o;
-    return myElementType == info.myElementType && myElementClass == info.myElementClass && myFileLanguage == info.myFileLanguage;
-  }
-
-  @Override
-  public int hashCode() {
-    return (myElementType == null ? 0 : myElementType.hashCode() * 31 * 31) +
-           31 * myElementClass.getName().hashCode() +
-           myFileLanguage.hashCode();
-  }
-
-  @Override
-  public String toString() {
-    return MoreObjects.toStringHelper(this)
-      .add("class", myElementClass)
-      .add("elementType", myElementType)
-      .add("fileLanguage", myFileLanguage)
-      .toString();
-  }
-
-  @NotNull
-  public Language getFileLanguage() {
-    return myFileLanguage;
-  }
-
-  public boolean isAcceptable(@NotNull PsiElement element) {
-    return myElementClass == element.getClass() && myElementType == PsiUtilCore.getElementType(element);
-  }
-
-  public static AnchorTypeInfo obtainInfo(@NotNull PsiElement element, @NotNull Language fileLanguage) {
-    return obtainInfo(element.getClass(), PsiUtilCore.getElementType(element), fileLanguage);
-  }
-
-  @NotNull
-  static AnchorTypeInfo obtainInfo(@NotNull Class elementClass, @Nullable IElementType elementType, @NotNull Language fileLanguage) {
-    return ourInterner.intern(new AnchorTypeInfo(elementClass, elementType, fileLanguage));
-  }
-}
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
new file mode 100644 (file)
index 0000000..5b7c186
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.psi.impl.smartPointers;
+
+import com.google.common.base.MoreObjects;
+import com.intellij.lang.Language;
+import com.intellij.openapi.util.Pair;
+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.util.PsiTreeUtil;
+import com.intellij.psi.util.PsiUtilCore;
+import com.intellij.util.containers.WeakInterner;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author peter
+ */
+public abstract class Identikit {
+  private static final WeakInterner<ByType> ourPlainInterner = new WeakInterner<ByType>();
+  private static final WeakInterner<ByAnchor> ourAnchorInterner = new WeakInterner<ByAnchor>();
+
+  @Nullable
+  public abstract PsiElement findPsiElement(@NotNull PsiFile file, int startOffset, int endOffset);
+
+  @NotNull
+  public abstract Language getFileLanguage();
+
+  public static ByType fromPsi(@NotNull PsiElement element, @NotNull Language fileLanguage) {
+    return fromTypes(element.getClass(), PsiUtilCore.getElementType(element), fileLanguage);
+  }
+
+  @Nullable
+  static Pair<ByAnchor, PsiElement> withAnchor(@NotNull PsiElement element, @NotNull Language fileLanguage) {
+    PsiUtilCore.ensureValid(element);
+    if (element.isPhysical()) {
+      for (SmartPointerAnchorProvider provider : SmartPointerAnchorProvider.EP_NAME.getExtensions()) {
+        PsiElement anchor = provider.getAnchor(element);
+        if (anchor != null && anchor.isPhysical() && provider.restoreElement(anchor) == element) {
+          ByAnchor anchorKit = new ByAnchor(fromPsi(element, fileLanguage), fromPsi(anchor, fileLanguage), provider);
+          return Pair.create(ourAnchorInterner.intern(anchorKit), anchor);
+        }
+      }
+    }
+    return null;
+  }
+
+  @NotNull
+  static ByType fromTypes(@NotNull Class elementClass, @Nullable IElementType elementType, @NotNull Language fileLanguage) {
+    return ourPlainInterner.intern(new ByType(elementClass, elementType, fileLanguage));
+  }
+
+  public static class ByType extends Identikit {
+    private final Class myElementClass;
+    private final IElementType myElementType;
+    private final Language myFileLanguage;
+
+    private ByType(@NotNull Class elementClass, @Nullable IElementType elementType, Language fileLanguage) {
+      myElementClass = elementClass;
+      myElementType = elementType;
+      myFileLanguage = fileLanguage;
+    }
+
+    @Nullable
+    @Override
+    public PsiElement findPsiElement(@NotNull PsiFile file, int startOffset, int endOffset) {
+      PsiElement anchor = file.getViewProvider().findElementAt(startOffset, myFileLanguage);
+      if (anchor == null && startOffset == file.getTextLength()) {
+        PsiElement lastChild = file.getViewProvider().getPsi(myFileLanguage).getLastChild();
+        if (lastChild != null) {
+          anchor = PsiTreeUtil.getDeepestLast(lastChild);
+        }
+      }
+      if (anchor == null) return null;
+
+      PsiElement result = findParent(startOffset, endOffset, anchor);
+      if (endOffset == startOffset) {
+        while (result == null && anchor.getTextRange().getStartOffset() == endOffset) {
+          anchor = PsiTreeUtil.prevLeaf(anchor, false);
+          if (anchor == null) break;
+
+          result = findParent(startOffset, endOffset, anchor);
+        }
+      }
+      return result;
+
+    }
+
+    @Nullable
+    private PsiElement findParent(int startOffset, int endOffset, PsiElement anchor) {
+      TextRange range = anchor.getTextRange();
+
+      if (range.getStartOffset() != startOffset) return null;
+      while (range.getEndOffset() < endOffset) {
+        anchor = anchor.getParent();
+        if (anchor == null || anchor.getTextRange() == null) {
+          return null;
+        }
+        range = anchor.getTextRange();
+      }
+
+      while (range.getEndOffset() == endOffset) {
+        if (isAcceptable(anchor)) {
+          return anchor;
+        }
+        anchor = anchor.getParent();
+        if (anchor == null || anchor.getTextRange() == null) break;
+        range = anchor.getTextRange();
+      }
+
+      return null;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (!(o instanceof ByType)) return false;
+
+      ByType info = (ByType)o;
+      return myElementType == info.myElementType && myElementClass == info.myElementClass && myFileLanguage == info.myFileLanguage;
+    }
+
+    @Override
+    public int hashCode() {
+      return (myElementType == null ? 0 : myElementType.hashCode() * 31 * 31) +
+             31 * myElementClass.getName().hashCode() +
+             myFileLanguage.hashCode();
+    }
+
+    @Override
+    public String toString() {
+      return MoreObjects.toStringHelper(this)
+        .add("class", myElementClass)
+        .add("elementType", myElementType)
+        .add("fileLanguage", myFileLanguage)
+        .toString();
+    }
+
+    @NotNull
+    public Language getFileLanguage() {
+      return myFileLanguage;
+    }
+
+    private boolean isAcceptable(@NotNull PsiElement element) {
+      return myElementClass == element.getClass() && myElementType == PsiUtilCore.getElementType(element);
+    }
+  }
+
+  static class ByAnchor extends Identikit {
+    private final ByType myElementInfo;
+    private final ByType myAnchorInfo;
+    private final SmartPointerAnchorProvider myAnchorProvider;
+
+    ByAnchor(ByType elementInfo, ByType anchorInfo, SmartPointerAnchorProvider anchorProvider) {
+      myElementInfo = elementInfo;
+      myAnchorInfo = anchorInfo;
+      myAnchorProvider = anchorProvider;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      if (this == o) return true;
+      if (!(o instanceof ByAnchor)) return false;
+
+      ByAnchor anchor = (ByAnchor)o;
+
+      if (!myElementInfo.equals(anchor.myElementInfo)) return false;
+      if (!myAnchorInfo.equals(anchor.myAnchorInfo)) return false;
+      if (!myAnchorProvider.equals(anchor.myAnchorProvider)) return false;
+
+      return true;
+    }
+
+    @Override
+    public int hashCode() {
+      return myElementInfo.hashCode();
+    }
+
+    @Nullable
+    @Override
+    public PsiElement findPsiElement(@NotNull PsiFile file, int startOffset, int endOffset) {
+      PsiElement anchor = myAnchorInfo.findPsiElement(file, startOffset, endOffset);
+      return anchor == null ? null : myAnchorProvider.restoreElement(anchor);
+    }
+
+    @NotNull
+    @Override
+    public Language getFileLanguage() {
+      return myAnchorInfo.getFileLanguage();
+    }
+  }
+
+}
index 11cb08d07c4e7677751055b0b2030bea30b38488..7c1c9f3d61275a04d307ba8e5ed1143827d327cd 100644 (file)
@@ -42,7 +42,7 @@ import java.util.List;
 class InjectedSelfElementInfo extends SmartPointerElementInfo {
   private final SmartPsiFileRange myInjectedFileRangeInHostFile;
   @Nullable private final AffixOffsets myAffixOffsets;
-  private final AnchorTypeInfo myType;
+  private final Identikit myType;
   @NotNull
   private final SmartPsiElementPointer<PsiLanguageInjectionHost> myHostContext;
 
@@ -60,7 +60,7 @@ class InjectedSelfElementInfo extends SmartPointerElementInfo {
     assert !(hostFile.getViewProvider() instanceof FreeThreadedFileViewProvider) : "hostContext parameter must not be and injected element: "+hostContext;
     SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(project);
     myInjectedFileRangeInHostFile = smartPointerManager.createSmartPsiFileRangePointer(hostFile, hostRange);
-    myType = AnchorTypeInfo.obtainInfo(injectedElement, LanguageUtil.getRootLanguage(containingFile));
+    myType = Identikit.fromPsi(injectedElement, LanguageUtil.getRootLanguage(containingFile));
 
     int startAffixIndex = -1;
     int startAffixOffset = -1;
@@ -114,7 +114,7 @@ class InjectedSelfElementInfo extends SmartPointerElementInfo {
     ProperTextRange rangeInInjected = hostToInjected(true, segment, injectedPsi, myAffixOffsets);
     if (rangeInInjected == null) return null;
 
-    return SelfElementInfo.findElementInside(injectedPsi, rangeInInjected, myType);
+    return myType.findPsiElement(injectedPsi, rangeInInjected.getStartOffset(), rangeInInjected.getEndOffset());
   }
 
   private PsiFile getInjectedFileIn(@NotNull final PsiElement hostContext,
index 662060905a75c428cc65d64a020f1e6194c89ef7..82ee5eb1710d21e92304389ed73aaf2d5affaed1 100644 (file)
@@ -26,7 +26,6 @@ import com.intellij.openapi.util.*;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.*;
 import com.intellij.psi.impl.PsiDocumentManagerBase;
-import com.intellij.psi.util.PsiTreeUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -37,7 +36,7 @@ import java.util.List;
 */
 public class SelfElementInfo extends SmartPointerElementInfo {
   private static final FileDocumentManager ourFileDocManager = FileDocumentManager.getInstance();
-  protected volatile AnchorTypeInfo myType;
+  private volatile Identikit myIdentikit;
   protected final SmartPointerManagerImpl myManager;
   private final VirtualFile myFile;
   private final boolean myForInjected;
@@ -46,17 +45,28 @@ public class SelfElementInfo extends SmartPointerElementInfo {
 
   SelfElementInfo(@NotNull Project project,
                   @Nullable ProperTextRange range,
-                  @NotNull AnchorTypeInfo info,
+                  @NotNull Identikit identikit,
                   @NotNull PsiFile containingFile,
                   boolean forInjected) {
     myForInjected = forInjected;
-    myType = info;
+    myIdentikit = identikit;
 
     myManager = (SmartPointerManagerImpl)SmartPointerManager.getInstance(project);
     myFile = containingFile.getViewProvider().getVirtualFile();
     setRange(range);
   }
 
+  protected void switchToAnchor(@NotNull PsiElement element) {
+    Pair<Identikit.ByAnchor, PsiElement> pair = Identikit.withAnchor(element, myIdentikit.getFileLanguage());
+    if (pair != null) {
+      assert pair.first.hashCode() == myIdentikit.hashCode();
+      myIdentikit = pair.first;
+      setRange(pair.second.getTextRange());
+    } else {
+      setRange(element.getTextRange());
+    }
+  }
+
   void setRange(@Nullable Segment range) {
     if (range != null) {
       myStartOffset = range.getStartOffset();
@@ -96,7 +106,7 @@ public class SelfElementInfo extends SmartPointerElementInfo {
     PsiFile file = restoreFile();
     if (file == null || !file.isValid()) return null;
 
-    return findElementInside(file, segment, myType);
+    return myIdentikit.findPsiElement(file, segment.getStartOffset(), segment.getEndOffset());
   }
 
   @Nullable
@@ -112,63 +122,7 @@ public class SelfElementInfo extends SmartPointerElementInfo {
 
   @Override
   public PsiFile restoreFile() {
-    return restoreFileFromVirtual(getVirtualFile(), getProject(), myType.getFileLanguage());
-  }
-
-  @Nullable
-  public static PsiElement findElementInside(@NotNull PsiFile file, @NotNull Segment range, @NotNull AnchorTypeInfo type) {
-    return findElementInside(file, range.getStartOffset(), range.getEndOffset(), type);
-  }
-
-  @Nullable
-  public static PsiElement findElementInside(@NotNull PsiFile file,
-                                             int syncStartOffset,
-                                             int syncEndOffset,
-                                             @NotNull AnchorTypeInfo type) {
-    PsiElement anchor = file.getViewProvider().findElementAt(syncStartOffset, type.getFileLanguage());
-    if (anchor == null && syncStartOffset == file.getTextLength()) {
-      PsiElement lastChild = file.getViewProvider().getPsi(type.getFileLanguage()).getLastChild();
-      if (lastChild != null) {
-        anchor = PsiTreeUtil.getDeepestLast(lastChild);
-      }
-    }
-    if (anchor == null) return null;
-
-    PsiElement result = findParent(syncStartOffset, syncEndOffset, type, anchor);
-    if (syncEndOffset == syncStartOffset) {
-      while (result == null && anchor.getTextRange().getStartOffset() == syncEndOffset) {
-        anchor = PsiTreeUtil.prevLeaf(anchor, false);
-        if (anchor == null) break;
-
-        result = findParent(syncStartOffset, syncEndOffset, type, anchor);
-      }
-    }
-    return result;
-  }
-
-  @Nullable
-  private static PsiElement findParent(int syncStartOffset, int syncEndOffset, @NotNull AnchorTypeInfo type, PsiElement anchor) {
-    TextRange range = anchor.getTextRange();
-
-    if (range.getStartOffset() != syncStartOffset) return null;
-    while (range.getEndOffset() < syncEndOffset) {
-      anchor = anchor.getParent();
-      if (anchor == null || anchor.getTextRange() == null) {
-        return null;
-      }
-      range = anchor.getTextRange();
-    }
-
-    while (range.getEndOffset() == syncEndOffset) {
-      if (type.isAcceptable(anchor)) {
-        return anchor;
-      }
-      anchor = anchor.getParent();
-      if (anchor == null || anchor.getTextRange() == null) break;
-      range = anchor.getTextRange();
-    }
-
-    return null;
+    return restoreFileFromVirtual(getVirtualFile(), getProject(), myIdentikit.getFileLanguage());
   }
 
   @Override
@@ -227,14 +181,14 @@ public class SelfElementInfo extends SmartPointerElementInfo {
 
   @Override
   public int elementHashCode() {
-    return getVirtualFile().hashCode();
+    return getVirtualFile().hashCode() + myIdentikit.hashCode() * 31;
   }
 
   @Override
   public boolean pointsToTheSameElementAs(@NotNull final SmartPointerElementInfo other) {
     if (other instanceof SelfElementInfo) {
       final SelfElementInfo otherInfo = (SelfElementInfo)other;
-      if (!getVirtualFile().equals(other.getVirtualFile()) || myType != otherInfo.myType) return false;
+      if (!getVirtualFile().equals(other.getVirtualFile()) || myIdentikit != otherInfo.myIdentikit) return false;
 
       return ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
         @Override
@@ -292,6 +246,6 @@ public class SelfElementInfo extends SmartPointerElementInfo {
 
   @Override
   public String toString() {
-    return "psi:range=" + calcPsiRange() + ",type=" + myType;
+    return "psi:range=" + calcPsiRange() + ",type=" + myIdentikit;
   }
 }
index b1b82d34add24e95933f0fa2fe82b4395c1351a6..91bad35328c5eb34619fa686ac84f911e419e15b 100644 (file)
@@ -22,15 +22,17 @@ import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.diagnostic.Logger;
 import com.intellij.openapi.editor.Document;
 import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Comparing;
-import com.intellij.openapi.util.ProperTextRange;
-import com.intellij.openapi.util.Segment;
-import com.intellij.openapi.util.TextRange;
+import com.intellij.openapi.util.*;
 import com.intellij.openapi.vfs.VirtualFile;
 import com.intellij.psi.*;
 import com.intellij.psi.impl.FreeThreadedFileViewProvider;
 import com.intellij.psi.impl.PsiManagerEx;
+import com.intellij.psi.impl.source.PsiFileImpl;
+import com.intellij.psi.impl.source.PsiFileWithStubSupport;
 import com.intellij.psi.impl.source.tree.ForeignLeafPsiElement;
+import com.intellij.psi.stubs.IStubElementType;
+import com.intellij.psi.stubs.StubTree;
+import com.intellij.psi.tree.IStubFileElementType;
 import com.intellij.psi.util.PsiTreeUtil;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -182,7 +184,7 @@ class SmartPsiElementPointerImpl<E extends PsiElement> implements SmartPointerEx
       }
     }
 
-    SmartPointerElementInfo info = AnchorElementInfoFactory.createElementInfo(element, containingFile);
+    SmartPointerElementInfo info = createAnchorInfo(element, containingFile);
     if (info != null) {
       return info;
     }
@@ -202,7 +204,32 @@ class SmartPsiElementPointerImpl<E extends PsiElement> implements SmartPointerEx
     }
     ProperTextRange proper = ProperTextRange.create(elementRange);
 
-    return new SelfElementInfo(project, proper, AnchorTypeInfo.obtainInfo(element, LanguageUtil.getRootLanguage(element)), containingFile, forInjected);
+    return new SelfElementInfo(project, proper, Identikit.fromPsi(element, LanguageUtil.getRootLanguage(element)), containingFile, forInjected);
+  }
+
+  @Nullable
+  private static SmartPointerElementInfo createAnchorInfo(@NotNull PsiElement element, @NotNull PsiFile containingFile) {
+    if (element instanceof StubBasedPsiElement && containingFile instanceof PsiFileWithStubSupport) {
+      PsiFileWithStubSupport stubFile = (PsiFileWithStubSupport)containingFile;
+      StubTree stubTree = stubFile.getStubTree();
+      if (stubTree != null) {
+        // use stubs when tree is not loaded
+        StubBasedPsiElement stubPsi = (StubBasedPsiElement)element;
+        int stubId = PsiAnchor.calcStubIndex(stubPsi);
+        IStubElementType myStubElementType = stubPsi.getElementType();
+
+        IStubFileElementType elementTypeForStubBuilder = ((PsiFileImpl)containingFile).getElementTypeForStubBuilder();
+        if (stubId != -1 && elementTypeForStubBuilder != null) { // TemplateDataElementType is not IStubFileElementType
+          return new AnchorElementInfo(element, stubFile, stubId, myStubElementType);
+        }
+      }
+    }
+
+    Pair<Identikit.ByAnchor, PsiElement> pair = Identikit.withAnchor(element, LanguageUtil.getRootLanguage(containingFile));
+    if (pair != null) {
+      return new AnchorElementInfo(pair.second, containingFile, pair.first);
+    }
+    return null;
   }
 
   @NotNull
index 0ea1ce4cd6b30fa5fe03983ffb535a252513d51b..4f08cc1f34df80360389206e8fb6b439187d3c65 100644 (file)
@@ -42,7 +42,7 @@ class SmartPsiFileRangePointerImpl extends SmartPsiElementPointerImpl<PsiFile> i
       }
     }
     if (!forInjected && range.equals(containingFile.getTextRange())) return new FileElementInfo(containingFile);
-    return new SelfElementInfo(project, range, AnchorTypeInfo.obtainInfo(PsiElement.class, null, LanguageUtil.getRootLanguage(containingFile)), containingFile, forInjected);
+    return new SelfElementInfo(project, range, Identikit.fromTypes(PsiElement.class, null, LanguageUtil.getRootLanguage(containingFile)), containingFile, forInjected);
   }
 
   @Override
index 4bf8716b0c48bb78b7ecc9583f3259ac72b29048..7a513314f5c007c465af415609b97592d2ce4d84 100644 (file)
 
     <fileIndentOptionsProvider implementation="com.intellij.psi.codeStyle.DetectableIndentOptionsProvider" order="last"/>
     <editorNotificationProvider implementation="com.intellij.psi.codeStyle.autodetect.DetectedIndentOptionsNotificationProvider"/>
-    <smartPointerElementInfoFactory implementation="com.intellij.psi.impl.smartPointers.AnchorElementInfoFactory"/>
     <codeInsight.lineMarkerProvider language="" implementationClass="com.intellij.execution.lineMarker.RunLineMarkerProvider"/>
     <projectService serviceImplementation="com.intellij.execution.TestStateStorage"/>
     <editorActionHandler action="EditorEscape" implementationClass="com.intellij.refactoring.changeSignature.inplace.EscapeHandler" id="changeSignatureEscape" order="before hide-search"/>