From bf0869d51f0cb23f7b15567336a15852673789f7 Mon Sep 17 00:00:00 2001 From: Daniel Espendiller Date: Tue, 4 Aug 2020 17:25:29 +0200 Subject: [PATCH] WIP: form field navigation and linemarker for Twig --- .../templating/TwigLineMarkerProvider.java | 45 ++++++++++++++++++- .../variable/TwigTypeContainer.java | 14 ++++-- .../variable/resolver/FormFieldResolver.java | 11 +++-- .../resolver/holder/FormDataHolder.java | 16 +++++++ 4 files changed, 77 insertions(+), 9 deletions(-) diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigLineMarkerProvider.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigLineMarkerProvider.java index e2edc3f55..0e1dd0039 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigLineMarkerProvider.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigLineMarkerProvider.java @@ -110,6 +110,11 @@ public void collectSlowLineMarkers(@NotNull List psiElements, @NotNu if(lineOverwrites != null) { results.add(lineOverwrites); } + } else if(TwigPattern.getFunctionPattern("form_row", "form_widget").accepts(psiElement)) { + LineMarkerInfo lineOverwrites = attachFormTypeFields(psiElement); + if(lineOverwrites != null) { + results.add(lineOverwrites); + } } } } @@ -299,7 +304,45 @@ private LineMarkerInfo attachBlockOverwrites(@NotNull PsiElement psiElement, @No NavigationGutterIconBuilder builder = NavigationGutterIconBuilder.create(PhpIcons.OVERRIDES) .setTargets(new BlockOverwriteLazyValue(psiElement)) - .setTooltipText("Overwrites") + .setTooltipText("Navigate to form") + .setCellRenderer(new MyBlockListCellRenderer()); + + return builder.createLineMarkerInfo(firstChild); + } + + @Nullable + private LineMarkerInfo attachFormTypeFields(@NotNull PsiElement psiElement) { + PsiElement firstChild = psiElement.getFirstChild(); + if (firstChild == null) { + return null; + } + + PsiElement nextSiblingOfType = PsiElementUtils.getNextSiblingOfType(firstChild, PlatformPatterns.psiElement().withElementType(TwigTokenTypes.IDENTIFIER).beforeLeaf(PlatformPatterns.psiElement(TwigTokenTypes.RBRACE))); + if (nextSiblingOfType == null) { + return null; + } + + Collection twigTypeContainers = TwigTypeResolveUtil.resolveTwigMethodName(nextSiblingOfType, TwigTypeResolveUtil.formatPsiTypeNameWithCurrent(nextSiblingOfType)); + + Collection targets = new HashSet<>(); + + for (TwigTypeContainer twigTypeContainer : twigTypeContainers) { + Object dataHolder = twigTypeContainer.getDataHolder(); + if (dataHolder instanceof FormDataHolder && PhpElementsUtil.isInstanceOf(((FormDataHolder) dataHolder).getFormType(), "\\Symfony\\Component\\Form\\FormTypeInterface")) { + PsiElement field = ((FormDataHolder) dataHolder).getField(); + if (field != null) { + targets.add(field); + } + } + } + + if (targets.isEmpty()) { + return null; + } + + NavigationGutterIconBuilder builder = NavigationGutterIconBuilder.create(Symfony2Icons.FORM_TYPE_LINE_MARKER) + .setTargets(targets) + .setTooltipText("Navigate to form field") .setCellRenderer(new MyBlockListCellRenderer()); return builder.createLineMarkerInfo(psiElement); diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/variable/TwigTypeContainer.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/variable/TwigTypeContainer.java index 772580820..13e2956ed 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/variable/TwigTypeContainer.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/variable/TwigTypeContainer.java @@ -5,6 +5,7 @@ import com.jetbrains.php.lang.psi.elements.PhpNamedElement; import fr.adrienbrault.idea.symfony2plugin.templating.variable.dict.PsiVariable; import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -15,16 +16,20 @@ * @author Daniel Espendiller */ public class TwigTypeContainer { - + @Nullable private PhpNamedElement phpNamedElement; + + @Nullable private String stringElement; + + @Nullable private Object dataHolder; - public TwigTypeContainer(PhpNamedElement phpNamedElement) { + public TwigTypeContainer(@Nullable PhpNamedElement phpNamedElement) { this.phpNamedElement = phpNamedElement; } - public TwigTypeContainer(String stringElement) { + public TwigTypeContainer(@Nullable String stringElement) { this.stringElement = stringElement; } @@ -52,11 +57,12 @@ public static Collection fromCollection(Project project, Coll return twigTypeContainerList; } - public TwigTypeContainer withDataHolder(Object object) { + public TwigTypeContainer withDataHolder(@NotNull Object object) { this.dataHolder = object; return this; } + @Nullable public Object getDataHolder() { return dataHolder; } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/variable/resolver/FormFieldResolver.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/variable/resolver/FormFieldResolver.java index 1a0925a4a..8d433e193 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/variable/resolver/FormFieldResolver.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/variable/resolver/FormFieldResolver.java @@ -123,12 +123,15 @@ private static void attachFormFields(@Nullable MethodReference methodReference, } @NotNull - private static List getTwigTypeContainer(@NotNull Method method, @NotNull PhpClass formTypClass) { - List twigTypeContainers = new ArrayList<>(); + private static Collection getTwigTypeContainer(@NotNull Method method, @NotNull PhpClass formTypClass) { + Collection twigTypeContainers = new ArrayList<>(); for(MethodReference methodReference: FormUtil.getFormBuilderTypes(method)) { - String fieldName = PsiElementUtils.getMethodParameterAt(methodReference, 0); + if (fieldName == null) { + continue; + } + PsiElement psiElement = PsiElementUtils.getMethodParameterPsiElementAt(methodReference, 1); TwigTypeContainer twigTypeContainer = new TwigTypeContainer(fieldName); @@ -136,7 +139,7 @@ private static List getTwigTypeContainer(@NotNull Method meth if(psiElement != null) { PhpClass fieldType = FormUtil.getFormTypeClassOnParameter(psiElement); if(fieldType != null) { - twigTypeContainer.withDataHolder(new FormDataHolder(fieldType, formTypClass)); + twigTypeContainer.withDataHolder(new FormDataHolder(fieldType, formTypClass, psiElement)); } } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/variable/resolver/holder/FormDataHolder.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/variable/resolver/holder/FormDataHolder.java index 6b9027969..49a4c66b2 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/variable/resolver/holder/FormDataHolder.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/variable/resolver/holder/FormDataHolder.java @@ -1,7 +1,9 @@ package fr.adrienbrault.idea.symfony2plugin.templating.variable.resolver.holder; +import com.intellij.psi.PsiElement; import com.jetbrains.php.lang.psi.elements.PhpClass; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author Daniel Espendiller @@ -13,11 +15,20 @@ public class FormDataHolder { @NotNull private final PhpClass formType; + @Nullable + private PsiElement field; + public FormDataHolder(@NotNull PhpClass phpClass, @NotNull PhpClass formType) { this.phpClass = phpClass; this.formType = formType; } + public FormDataHolder(@NotNull PhpClass phpClass, @NotNull PhpClass formType, @NotNull PsiElement field) { + this.phpClass = phpClass; + this.formType = formType; + this.field = field; + } + @NotNull public PhpClass getPhpClass() { return phpClass; @@ -27,4 +38,9 @@ public PhpClass getPhpClass() { public PhpClass getFormType() { return formType; } + + @Nullable + public PsiElement getField() { + return this.field; + } }