package fr.adrienbrault.idea.symfony2plugin.routing; import com.intellij.codeInsight.daemon.LineMarkerInfo; import com.intellij.codeInsight.daemon.LineMarkerProvider; import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo; import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiInvalidElementAccessException; import com.jetbrains.php.lang.psi.elements.PhpClass; import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons; import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; import fr.adrienbrault.idea.symfony2plugin.doctrine.metadata.util.DoctrineMetadataUtil; import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil; import fr.adrienbrault.idea.symfony2plugin.util.resource.FileResourceUtil; import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.yaml.psi.*; import java.util.Collection; import java.util.List; /** * @author Daniel Espendiller <daniel@espendiller.net> */ public class YamlLineMarkerProvider implements LineMarkerProvider { @Override public void collectSlowLineMarkers(@NotNull List<PsiElement> psiElements, @NotNull Collection<LineMarkerInfo> lineMarkerInfos) { if(psiElements.size() == 0 || !Symfony2ProjectComponent.isEnabled(psiElements.get(0))) { return; } for(PsiElement psiElement : psiElements) { attachRouteActions(lineMarkerInfos, psiElement); attachEntityClass(lineMarkerInfos, psiElement); attachRelationClass(lineMarkerInfos, psiElement); if(psiElement instanceof YAMLFile) { RelatedItemLineMarkerInfo<PsiElement> lineMarker = FileResourceUtil.getFileImplementsLineMarker((PsiFile) psiElement); if(lineMarker != null) { lineMarkerInfos.add(lineMarker); } } } } private void attachEntityClass(Collection<LineMarkerInfo> lineMarkerInfos, PsiElement psiElement) { if(psiElement instanceof YAMLKeyValue && psiElement.getParent() instanceof YAMLMapping && psiElement.getParent().getParent() instanceof YAMLDocument) { PsiFile containingFile; try { containingFile = psiElement.getContainingFile(); } catch (PsiInvalidElementAccessException e) { return; } String fileName = containingFile.getName(); if(isMetadataFile(fileName)) { String keyText = ((YAMLKeyValue) psiElement).getKeyText(); if(StringUtils.isNotBlank(keyText)) { Collection<PhpClass> phpClasses = PhpElementsUtil.getClassesInterface(psiElement.getProject(), keyText); if(phpClasses.size() > 0) { NavigationGutterIconBuilder<PsiElement> builder = NavigationGutterIconBuilder.create(Symfony2Icons.DOCTRINE_LINE_MARKER). setTargets(phpClasses). setTooltipText("Navigate to class"); lineMarkerInfos.add(builder.createLineMarkerInfo(psiElement)); } } } } } private void attachRouteActions(Collection<LineMarkerInfo> lineMarkerInfos, PsiElement psiElement) { /* * foo: * defaults: { _controller: "Bundle:Foo:Bar" } * defaults: * _controller: "Bundle:Foo:Bar" */ if(psiElement instanceof YAMLKeyValue && psiElement.getParent() instanceof YAMLMapping) { YAMLKeyValue yamlKeyValue = YamlHelper.getYamlKeyValue((YAMLKeyValue) psiElement, "defaults"); if(yamlKeyValue != null) { final YAMLValue container = yamlKeyValue.getValue(); if(container instanceof YAMLMapping) { YAMLKeyValue yamlKeyValueController = YamlHelper.getYamlKeyValue(container, "_controller", true); if(yamlKeyValueController != null) { PsiElement[] methods = RouteHelper.getMethodsOnControllerShortcut(psiElement.getProject(), yamlKeyValueController.getValueText()); if(methods.length > 0) { NavigationGutterIconBuilder<PsiElement> builder = NavigationGutterIconBuilder.create(Symfony2Icons.TWIG_CONTROLLER_LINE_MARKER). setTargets(methods). setTooltipText("Navigate to action"); lineMarkerInfos.add(builder.createLineMarkerInfo(psiElement)); } } } } } } @Nullable @Override public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement psiElement) { return null; } /** * Set linemarker for targetEntity in possible yaml entity files * * foo: * targetEntity: Class */ private void attachRelationClass(Collection<LineMarkerInfo> lineMarkerInfos, PsiElement psiElement) { if(!(psiElement instanceof YAMLKeyValue)) { return; } String keyText = ((YAMLKeyValue) psiElement).getKeyText(); if(!(keyText.equalsIgnoreCase("targetEntity") || keyText.equalsIgnoreCase("targetDocument"))) { return; } String valueText = ((YAMLKeyValue) psiElement).getValueText(); if(StringUtils.isBlank(valueText)) { return; } Collection<PhpClass> classesInterface = DoctrineMetadataUtil.getClassInsideScope(psiElement, valueText); if(classesInterface.size() == 0) { return; } // get relation key PsiElement parent = psiElement.getParent(); if(parent != null) { PsiElement parent1 = parent.getParent(); if(parent1 != null) { NavigationGutterIconBuilder<PsiElement> builder = NavigationGutterIconBuilder.create(Symfony2Icons.DOCTRINE_LINE_MARKER). setTargets(classesInterface). setTooltipText("Navigate to file"); lineMarkerInfos.add(builder.createLineMarkerInfo(parent1)); } } } private boolean isMetadataFile(String fileName) { fileName = fileName.toLowerCase(); return fileName.endsWith("orm.yml") || fileName.endsWith("odm.yml") || fileName.endsWith("mongodb.yml") || fileName.endsWith("couchdb.yml") || fileName.endsWith("document.yml"); } }