package com.siberika.idea.pascal.lang; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementResolveResult; import com.intellij.psi.PsiNameIdentifierOwner; import com.intellij.psi.PsiPolyVariantReferenceBase; import com.intellij.psi.ResolveResult; import com.intellij.psi.impl.source.resolve.ResolveCache; import com.intellij.util.ArrayUtil; import com.siberika.idea.pascal.ide.actions.SectionToggle; import com.siberika.idea.pascal.lang.parser.NamespaceRec; import com.siberika.idea.pascal.lang.psi.PascalNamedElement; import com.siberika.idea.pascal.lang.psi.impl.PasField; import com.siberika.idea.pascal.lang.psi.impl.PasRoutineImplDeclImpl; import com.siberika.idea.pascal.lang.references.PasReferenceUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; /** * Date: 3/13/13 * Author: George Bakhtadze */ public class PascalReference extends PsiPolyVariantReferenceBase<PascalNamedElement> { //PsiReferenceBase<PascalNamedElement> { private static final Logger LOG = Logger.getInstance(PascalReference.class); private final String key; public PascalReference(@NotNull PsiElement element, TextRange textRange) { super((PascalNamedElement) element, textRange); key = element.getText().substring(textRange.getStartOffset(), textRange.getEndOffset()); } @NotNull @Override public ResolveResult[] multiResolve(boolean incompleteCode) { final ResolveCache resolveCache = ResolveCache.getInstance(myElement.getProject()); return resolveCache.resolveWithCaching(this, Resolver.INSTANCE, true, incompleteCode, myElement.getContainingFile()); // return Resolver.INSTANCE.resolve(this, incompleteCode); } @Nullable @Override public PsiElement resolve() { ResolveResult[] resolveResults = multiResolve(false); return resolveResults.length > 0 ? resolveResults[0].getElement() : null; } @NotNull @Override public Object[] getVariants() { return ArrayUtil.EMPTY_OBJECT_ARRAY; } private static class Resolver implements ResolveCache.PolyVariantResolver<PascalReference> { public static final Resolver INSTANCE = new Resolver(); @NotNull @Override public ResolveResult[] resolve(@NotNull PascalReference pascalReference, boolean incompleteCode) { return doResolve(pascalReference.getElement(), pascalReference.key, incompleteCode); } static ResolveResult[] doResolve(@NotNull PascalNamedElement named, @NotNull String key, boolean incompleteCode) { if (PasField.DUMMY_IDENTIFIER.equals(key)) { return ResolveResult.EMPTY_ARRAY; } if (named.getParent() instanceof PasRoutineImplDeclImpl) { return resolveRoutineImpl((PasRoutineImplDeclImpl) named.getParent()); } final Collection<PasField> references = PasReferenceUtil.resolveExpr(null, NamespaceRec.fromElement(named), PasField.TYPES_ALL, true, 0); // return only first reference for (PasField el : references) { if (el != null) { PsiElement element = el.target != null ? el.target : el.getElement(); if (element != null) { return createResults(element); } } else { LOG.info("ERROR: null resolved for " + named); } } return ResolveResult.EMPTY_ARRAY; } private static ResolveResult[] resolveRoutineImpl(PasRoutineImplDeclImpl routine) { PsiElement decl = SectionToggle.retrieveDeclaration(routine, true); if (decl != null) { return createResults(decl); } else { decl = SectionToggle.getRoutineForwardDeclaration(routine); return decl != null ? createResults(decl) : ResolveResult.EMPTY_ARRAY; } } private static ResolveResult[] createResults(PsiElement element) { return PsiElementResolveResult.createResults(new PsiElement[]{element}); } } @Override public boolean isReferenceTo(PsiElement element) { final ResolveResult[] results = multiResolve(false); for (ResolveResult result : results) { if (getElement().getManager().areElementsEquivalent(getNamedElement(result.getElement()), getNamedElement(element))) { return true; } } return false; } private PsiElement getNamedElement(PsiElement element) { if (element instanceof PsiNameIdentifierOwner) { return ((PsiNameIdentifierOwner) element).getNameIdentifier(); } return element; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PascalReference that = (PascalReference) o; if (!getElement().equals(that.getElement())) return false; if (!key.equals(that.key)) return false; return true; } @Override public int hashCode() { int result = key.hashCode(); result = 31 * result + getElement().hashCode(); return result; } }