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; } } }