Created
August 2, 2016 16:17
-
-
Save artspb/a1f0546f65135740c6c53ccbeb1f7e53 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.espend.idea.php.annotation.reference; | |
import com.intellij.openapi.util.TextRange; | |
import com.intellij.patterns.PlatformPatterns; | |
import com.intellij.psi.*; | |
import com.intellij.util.ArrayUtil; | |
import com.intellij.util.IncorrectOperationException; | |
import com.intellij.util.ProcessingContext; | |
import com.jetbrains.php.PhpIndex; | |
import com.jetbrains.php.lang.documentation.phpdoc.lexer.PhpDocTokenTypes; | |
import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag; | |
import com.jetbrains.php.lang.psi.PhpPsiUtil; | |
import com.jetbrains.php.lang.psi.elements.PhpClass; | |
import com.jetbrains.php.lang.psi.elements.PhpNamedElement; | |
import com.jetbrains.php.lang.psi.elements.PhpUse; | |
import de.espend.idea.php.annotation.util.AnnotationUtil; | |
import de.espend.idea.php.annotation.util.PluginUtil; | |
import org.jetbrains.annotations.NotNull; | |
import org.jetbrains.annotations.Nullable; | |
/** | |
* @author Daniel Espendiller <[email protected]> | |
*/ | |
public class DocTagNameAnnotationReferenceContributor extends PsiReferenceContributor { | |
@Override | |
public void registerReferenceProviders(PsiReferenceRegistrar psiReferenceRegistrar) { | |
/** | |
* Our main reference provider to attach DocBlocTag to their use declaration | |
* This one resolve the "Optimize Usage" issues | |
* | |
* "@Template()", "@ORM\PostPersist()" | |
*/ | |
psiReferenceRegistrar.registerReferenceProvider( | |
PlatformPatterns.psiElement(PhpDocTag.class), | |
new PsiReferenceProvider() { | |
@NotNull | |
@Override | |
public PsiReference[] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) { | |
if(!PluginUtil.isEnabled(element) || !(element instanceof PhpDocTag)) { | |
return new PsiReference[0]; | |
} | |
return new PsiReference[] { | |
new PhpDocTagReference((PhpDocTag) element), | |
new PsiReference() { | |
@Override | |
public PsiElement getElement() { | |
return element; | |
} | |
@Override | |
public TextRange getRangeInElement() { | |
final PsiElement parent = element.getFirstChild().getNextSibling(); | |
final PsiElement token = PhpPsiUtil.getChildOfType(parent, PhpDocTokenTypes.DOC_TEXT).getNextSibling(); | |
final int startOffset = parent.getStartOffsetInParent() + token.getStartOffsetInParent(); | |
return new TextRange(startOffset, startOffset + token.getTextLength()); | |
} | |
@Nullable | |
@Override | |
public PsiElement resolve() { | |
return PhpIndex.getInstance(element.getProject()).getClassByName("testA"); | |
} | |
@NotNull | |
@Override | |
public String getCanonicalText() { | |
return ((PhpClass) resolve()).getFQN(); | |
} | |
@Override | |
public PsiElement handleElementRename(String s) throws IncorrectOperationException { | |
return element; | |
} | |
@Override | |
public PsiElement bindToElement(@NotNull PsiElement psiElement) throws IncorrectOperationException { | |
throw new UnsupportedOperationException(); | |
} | |
@Override | |
public boolean isReferenceTo(PsiElement psiElement) { | |
return resolve() == psiElement; | |
} | |
@NotNull | |
@Override | |
public Object[] getVariants() { | |
return ArrayUtil.EMPTY_OBJECT_ARRAY; | |
} | |
@Override | |
public boolean isSoft() { | |
return false; | |
} | |
} | |
}; | |
} | |
} | |
); | |
} | |
private static class PhpDocTagReference extends PsiPolyVariantReferenceBase<PhpDocTag> { | |
public PhpDocTagReference(PhpDocTag psiElement) { | |
super(psiElement); | |
} | |
@Override | |
public boolean isReferenceTo(PsiElement element) { | |
// use Doctrine\ORM\Mapping as "ORM"; | |
if (element instanceof PhpUse) { | |
String useName = ((PhpUse) element).getName(); | |
String docUseName = getDocBlockName(); | |
if(useName.equals(docUseName)) { | |
return true; | |
} | |
} | |
// eg for "Optimize Imports" | |
// attach reference to @Template() | |
// reference can also point to a namespace e.g. @Annotation\Exclude() | |
if (element instanceof PhpNamedElement) { | |
if(((PhpNamedElement) element).getName().equals(getDocBlockName())) { | |
return true; | |
} | |
} | |
return false; | |
} | |
/** | |
* We need to strip @ char before DocTag @Test, @Test\Foo | |
* | |
* @return TextRange of DocTag without @ char | |
*/ | |
public TextRange getRangeInElement() { | |
String tagName = getElement().getName(); | |
int rangeStart = 0; | |
int rangeEnd = tagName.length(); | |
// remove DocTag "@" char | |
// it should always be true, check for security reason | |
if(tagName.startsWith("@")) { | |
rangeStart = 1; | |
tagName = tagName.substring(1); | |
} | |
// "@ORM\PostPersist()" | |
// only on alias and namespace use main ns | |
if(tagName.contains("\\")) { | |
rangeEnd = tagName.indexOf("\\") + rangeStart; | |
} | |
return new TextRange(rangeStart, rangeEnd); | |
} | |
@NotNull | |
@Override | |
public ResolveResult[] multiResolve(boolean b) { | |
PhpClass phpClass = AnnotationUtil.getAnnotationReference(getElement()); | |
if(phpClass == null) { | |
return new ResolveResult[0]; | |
} | |
return new ResolveResult[] { new PsiElementResolveResult(phpClass) }; | |
} | |
@NotNull | |
@Override | |
public Object[] getVariants() { | |
return new Object[0]; | |
} | |
/** | |
* Get the class alias | |
* | |
* "@Template()" | |
* "@ORM\PostPersist()" | |
*/ | |
private String getDocBlockName() { | |
String docBlockName = getElement().getName(); | |
if(docBlockName.startsWith("@")) { | |
docBlockName = docBlockName.substring(1); | |
} | |
if(docBlockName.contains("\\")) { | |
docBlockName = docBlockName.substring(0, docBlockName.indexOf("\\")); | |
} | |
return docBlockName; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
thx, for example. a new playground ;)