package fr.adrienbrault.idea.symfony2plugin.doctrine.metadata.type;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.patterns.XmlPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.php.lang.psi.elements.Field;
import com.jetbrains.php.lang.psi.elements.PhpClass;
import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProvider;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrar;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrarParameter;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.utils.GotoCompletionUtil;
import fr.adrienbrault.idea.symfony2plugin.config.doctrine.DoctrineStaticTypeLookupBuilder;
import fr.adrienbrault.idea.symfony2plugin.config.yaml.YamlElementPatternHelper;
import fr.adrienbrault.idea.symfony2plugin.doctrine.metadata.DoctrineMetadataPattern;
import fr.adrienbrault.idea.symfony2plugin.doctrine.metadata.type.util.DoctrineMetadataTypeUtil;
import fr.adrienbrault.idea.symfony2plugin.doctrine.metadata.util.DoctrineMetadataUtil;
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.yaml.psi.YAMLKeyValue;
import java.util.*;
/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class DoctrineTypeGotoCompletionRegistrar implements GotoCompletionRegistrar {
@Override
public void register(GotoCompletionRegistrarParameter registrar) {
// <field type="string" />
registrar.register(
XmlPatterns.psiElement().withParent(DoctrineMetadataPattern.getFieldType()), psiElement -> new MyTypeGotoCompletionProvider(psiElement) {
@Nullable
@Override
protected String getElementText(@NotNull PsiElement element) {
return GotoCompletionUtil.getXmlAttributeValue(element);
}
});
// yml files
registrar.register(
YamlElementPatternHelper.getOrmSingleLineScalarKey("type"), psiElement -> new MyTypeGotoCompletionProvider(psiElement) {
@Nullable
@Override
protected String getElementText(@NotNull PsiElement element) {
String text = PsiElementUtils.trimQuote(element.getText());
if(StringUtils.isBlank(text)) {
return null;
}
return text;
}
}
);
// <field name="id" />
registrar.register(
XmlPatterns.psiElement().withParent(XmlPatterns.or(DoctrineMetadataPattern.getFieldName(), DoctrineMetadataPattern.getFieldNameRelation())), psiElement -> new MyFieldNameGotoCompletionProvider(psiElement) {
@Nullable
@Override
protected String getElementText(@NotNull PsiElement element) {
return GotoCompletionUtil.getXmlAttributeValue(element);
}
}
);
// fields:
// i<caret>d: []
registrar.register(
DoctrineMetadataPattern.getYamlFieldName(), MyYamlFieldNameGotoCompletionProvider::new
);
}
private abstract static class MyTypeGotoCompletionProvider extends GotoCompletionProvider {
public MyTypeGotoCompletionProvider(PsiElement element) {
super(element);
}
@NotNull
@Override
public Collection<LookupElement> getLookupElements() {
final Collection<LookupElement> lookupElements = new ArrayList<>();
Collection<String> typeClassesByScopeWithAllFallback = DoctrineMetadataTypeUtil.getTypeClassesByScopeWithAllFallback(getElement());
DoctrineMetadataTypeUtil.visitType(getProject(), typeClassesByScopeWithAllFallback, pair -> {
lookupElements.add(
LookupElementBuilder.create(pair.getSecond())
.withIcon(Symfony2Icons.DOCTRINE)
.withTypeText(pair.getSecond(), true)
);
return true;
});
if(typeClassesByScopeWithAllFallback.contains(DoctrineMetadataTypeUtil.DBAL_TYPE)) {
DoctrineStaticTypeLookupBuilder.fillOrmLookupElementsWithStatic(lookupElements);
}
return lookupElements;
}
@NotNull
@Override
public Collection<PsiElement> getPsiTargets(PsiElement element) {
final String value = getElementText(element);
if(value == null) {
return Collections.emptyList();
}
final Collection<PsiElement> psiElements = new ArrayList<>();
DoctrineMetadataTypeUtil.visitType(getProject(), DoctrineMetadataTypeUtil.getTypeClassesByScopeWithAllFallback(element), pair -> {
if (pair.getSecond().equalsIgnoreCase(value)) {
psiElements.add(pair.getFirst());
}
return true;
});
return psiElements;
}
@Nullable
abstract protected String getElementText(@NotNull PsiElement element);
}
private abstract static class MyFieldNameGotoCompletionProvider extends GotoCompletionProvider {
public MyFieldNameGotoCompletionProvider(PsiElement element) {
super(element);
}
@NotNull
@Override
public Collection<LookupElement> getLookupElements() {
String modelNameInScope = DoctrineMetadataUtil.findModelNameInScope(getElement());
if(modelNameInScope == null) {
return Collections.emptyList();
}
Set<String> unique = new HashSet<>();
Collection<LookupElement> lookupElements = new ArrayList<>();
for (PhpClass phpClass : PhpElementsUtil.getClassesInterface(getProject(), modelNameInScope)) {
for (Field field : phpClass.getFields()) {
if(field.isConstant() || unique.contains(field.getName())) {
continue;
}
String name = field.getName();
unique.add(name);
lookupElements.add(LookupElementBuilder.create(name).withIcon(field.getIcon()).withTypeText(phpClass.getPresentableFQN(), true));
}
}
return lookupElements;
}
@NotNull
@Override
public Collection<PsiElement> getPsiTargets(PsiElement element) {
final String elementText = getElementText(element);
if(elementText == null) {
return Collections.emptyList();
}
String modelNameInScope = DoctrineMetadataUtil.findModelNameInScope(getElement());
if(modelNameInScope == null) {
return Collections.emptyList();
}
final Collection<PsiElement> psiElements = new ArrayList<>();
for (PhpClass phpClass : PhpElementsUtil.getClassesInterface(getProject(), modelNameInScope)) {
psiElements.addAll(ContainerUtil.filter(phpClass.getFields(), field ->
!field.isConstant() && elementText.equals(field.getName()))
);
}
return psiElements;
}
@Nullable
abstract protected String getElementText(@NotNull PsiElement element);
}
private static class MyYamlFieldNameGotoCompletionProvider extends MyFieldNameGotoCompletionProvider {
public MyYamlFieldNameGotoCompletionProvider(PsiElement psiElement) {
super(psiElement);
}
@NotNull
public Collection<LookupElement> getLookupElements() {
return Collections.emptyList();
}
@Nullable
@Override
protected String getElementText(@NotNull PsiElement element) {
PsiElement parent = element.getParent();
if(!(parent instanceof YAMLKeyValue)) {
return null;
}
String keyText = ((YAMLKeyValue) parent).getKeyText();
if(StringUtils.isBlank(keyText)) {
return null;
}
return keyText;
}
}
}