From 9629018237614897c8092a5d617f3eab07d5b09b Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 14 Nov 2016 09:28:48 +0100 Subject: [PATCH] smart pointers for different element types should have different hash codes --- .../psi/impl/compiled/ClsElementImpl.java | 11 +- .../SmartPsiElementPointersTest.java | 5 + .../src/com/intellij/psi/PsiAnchor.java | 10 +- .../impl/smartPointers/AnchorElementInfo.java | 35 +-- .../AnchorElementInfoFactory.java | 70 ------ .../impl/smartPointers/AnchorTypeInfo.java | 84 ------- .../psi/impl/smartPointers/Identikit.java | 208 ++++++++++++++++++ .../InjectedSelfElementInfo.java | 6 +- .../impl/smartPointers/SelfElementInfo.java | 84 ++----- .../SmartPsiElementPointerImpl.java | 39 +++- .../SmartPsiFileRangePointerImpl.java | 2 +- .../src/META-INF/LangExtensions.xml | 1 - 12 files changed, 284 insertions(+), 271 deletions(-) delete mode 100644 platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfoFactory.java delete mode 100644 platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorTypeInfo.java create mode 100644 platform/core-impl/src/com/intellij/psi/impl/smartPointers/Identikit.java diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsElementImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsElementImpl.java index b9a511698371..d6a7710fa78e 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsElementImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsElementImpl.java @@ -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 myMirror; + private volatile Pair 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 mirror = myMirror; - return mirror == null ? null : SelfElementInfo.findElementInside(mirrorFile, mirror.first, mirror.second); + Pair 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 void setMirror(@Nullable T stub, @Nullable T mirror) throws InvalidMirrorException { 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 246dc65d43e7..72d1fefdcf9c 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 @@ -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()); + } + } diff --git a/platform/core-impl/src/com/intellij/psi/PsiAnchor.java b/platform/core-impl/src/com/intellij/psi/PsiAnchor.java index 3e3b6ca9bca1..e6f497ffa6b4 100644 --- a/platform/core-impl/src/com/intellij/psi/PsiAnchor.java +++ b/platform/core-impl/src/com/intellij/psi/PsiAnchor.java @@ -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 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 db1f92bc4598..6ff8ae16e48f 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 @@ -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 index 0fc806b77b79..000000000000 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfoFactory.java +++ /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 index 6702054f51eb..000000000000 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/AnchorTypeInfo.java +++ /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 ourInterner = new WeakInterner(); - 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 index 000000000000..5b7c186566b7 --- /dev/null +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/Identikit.java @@ -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 ourPlainInterner = new WeakInterner(); + private static final WeakInterner ourAnchorInterner = new WeakInterner(); + + @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 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(); + } + } + +} diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java index 11cb08d07c4e..7c1c9f3d6127 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java @@ -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 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, 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 662060905a75..82ee5eb1710d 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 @@ -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 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() { @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; } } 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 b1b82d34add2..91bad35328c5 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 @@ -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 implements SmartPointerEx } } - SmartPointerElementInfo info = AnchorElementInfoFactory.createElementInfo(element, containingFile); + SmartPointerElementInfo info = createAnchorInfo(element, containingFile); if (info != null) { return info; } @@ -202,7 +204,32 @@ class SmartPsiElementPointerImpl 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 pair = Identikit.withAnchor(element, LanguageUtil.getRootLanguage(containingFile)); + if (pair != null) { + return new AnchorElementInfo(pair.second, containingFile, pair.first); + } + return null; } @NotNull diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiFileRangePointerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiFileRangePointerImpl.java index 0ea1ce4cd6b3..4f08cc1f34df 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiFileRangePointerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiFileRangePointerImpl.java @@ -42,7 +42,7 @@ class SmartPsiFileRangePointerImpl extends SmartPsiElementPointerImpl 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 diff --git a/platform/platform-resources/src/META-INF/LangExtensions.xml b/platform/platform-resources/src/META-INF/LangExtensions.xml index 4bf8716b0c48..7a513314f5c0 100644 --- a/platform/platform-resources/src/META-INF/LangExtensions.xml +++ b/platform/platform-resources/src/META-INF/LangExtensions.xml @@ -953,7 +953,6 @@ -