Skip to content

Instantly share code, notes, and snippets.

@artspb
Created August 2, 2016 16:17
Show Gist options
  • Save artspb/a1f0546f65135740c6c53ccbeb1f7e53 to your computer and use it in GitHub Desktop.
Save artspb/a1f0546f65135740c6c53ccbeb1f7e53 to your computer and use it in GitHub Desktop.
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;
}
}
}
@Haehnchen
Copy link

thx, for example. a new playground ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment