package com.jetbrains.lang.dart.psi;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReference;
import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.psi.xml.XmlTag;
import com.intellij.util.ProcessingContext;
import com.intellij.xml.util.HtmlUtil;
import com.jetbrains.lang.dart.util.DartResolveUtil;
import com.jetbrains.lang.dart.util.DartUrlResolver;
import com.jetbrains.lang.dart.util.PubspecYamlUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Resolves path in <code><script src="packages/browser/dart.js"/></code> to base Dart <code>packages</code> folder because relative symlinked <code>packages</code> folder is excluded.<br/>
* Another example: <code><link rel="import" href="packages/click_counter/click_counter.html"></code> is resolved to ./lib/click_counter.html if 'click_counter' is a Dart project name in pubspec.yaml
*/
public class DartPackagePathReferenceProvider extends PsiReferenceProvider {
public static ElementFilter getFilter() {
return new ElementFilter() {
@Override
public boolean isAcceptable(Object _element, PsiElement context) {
if (!(_element instanceof PsiElement)) return false;
final PsiElement element = (PsiElement)_element;
final PsiElement parentElement = element.getParent();
final PsiFile file = element.getContainingFile().getOriginalFile();
final VirtualFile vFile = file.getVirtualFile();
return vFile != null &&
HtmlUtil.hasHtml(file) &&
parentElement instanceof XmlAttribute &&
canContainDartPackageReference(((XmlAttribute)parentElement).getParent().getLocalName(),
((XmlAttribute)parentElement).getName()) &&
PubspecYamlUtil.findPubspecYamlFile(element.getProject(), vFile) != null;
}
@Override
public boolean isClassAcceptable(Class hintClass) {
return true;
}
};
}
private static boolean canContainDartPackageReference(@Nullable final String tagName, @Nullable final String attrName) {
return ("link".equalsIgnoreCase(tagName) && "href".equalsIgnoreCase(attrName)) ||
("script".equalsIgnoreCase(tagName) && "src".equalsIgnoreCase(attrName)) ||
("img".equalsIgnoreCase(tagName) && "src".equalsIgnoreCase(attrName));
}
@NotNull
@Override
public PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement, @NotNull ProcessingContext context) {
if (!(psiElement instanceof XmlAttributeValue) || !HtmlUtil.isHtmlFile(psiElement.getContainingFile())) return PsiReference.EMPTY_ARRAY;
final PsiElement parent = psiElement.getParent();
if (!(parent instanceof XmlAttribute)) return PsiReference.EMPTY_ARRAY;
final XmlTag tag = ((XmlAttribute)parent).getParent();
if (tag == null) return PsiReference.EMPTY_ARRAY;
final VirtualFile file = DartResolveUtil.getRealVirtualFile(psiElement.getContainingFile());
if (file == null) return PsiReference.EMPTY_ARRAY;
if (!canContainDartPackageReference(tag.getName(), ((XmlAttribute)parent).getName())) return PsiReference.EMPTY_ARRAY;
if (PubspecYamlUtil.findPubspecYamlFile(psiElement.getProject(), file) == null) return PsiReference.EMPTY_ARRAY;
return getDartPackageReferences(psiElement, DartUrlResolver.getInstance(psiElement.getProject(), file));
}
private static FileReference[] getDartPackageReferences(@NotNull final PsiElement psiElement,
@NotNull final DartUrlResolver dartResolver) {
final TextRange textRange = ElementManipulators.getValueTextRange(psiElement);
final String referenceText = psiElement.getText().substring(textRange.getStartOffset(), textRange.getEndOffset());
final FileReferenceSet referenceSet = new FileReferenceSet(referenceText, psiElement, textRange.getStartOffset(), null, true) {
public FileReference createFileReference(final TextRange range, final int index, final String text) {
return new DartPackageAwareFileReference(this, range, index, text, dartResolver);
}
};
return referenceSet.getAllReferences();
}
}