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;
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
@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
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 {
}).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());
+ }
+
}
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;
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
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();
PsiFile psiFile = getFile();
if (psiFile == null || !psiFile.isValid()) return null;
- return SelfElementInfo.findElementInside(psiFile, myStartOffset, myEndOffset, myInfo);
+ return myInfo.findPsiElement(psiFile, myStartOffset, myEndOffset);
}
@Override
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);
}
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;
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
}
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);
}
+++ /dev/null
-/*
- * 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;
- }
-}
+++ /dev/null
-/*
- * 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));
- }
-}
--- /dev/null
+/*
+ * 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();
+ }
+ }
+
+}
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;
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;
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,
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;
*/
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;
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();
PsiFile file = restoreFile();
if (file == null || !file.isValid()) return null;
- return findElementInside(file, segment, myType);
+ return myIdentikit.findPsiElement(file, segment.getStartOffset(), segment.getEndOffset());
}
@Nullable
@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
@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
@Override
public String toString() {
- return "psi:range=" + calcPsiRange() + ",type=" + myType;
+ return "psi:range=" + calcPsiRange() + ",type=" + myIdentikit;
}
}
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;
}
}
- SmartPointerElementInfo info = AnchorElementInfoFactory.createElementInfo(element, containingFile);
+ SmartPointerElementInfo info = createAnchorInfo(element, containingFile);
if (info != null) {
return info;
}
}
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
}
}
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
<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"/>