diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigTemplateCompletionContributor.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigTemplateCompletionContributor.java index 5976f12b9..ef5dcda14 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigTemplateCompletionContributor.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/TwigTemplateCompletionContributor.java @@ -1053,18 +1053,23 @@ public boolean accepts(@NotNull String s, ProcessingContext processingContext) { continue; } + PsiElement element = entry.getValue().getElement(); + if (element == null) { + continue; + } + String typeText = null; - Collection formTypeFromFormFactory = FormFieldResolver.getFormTypeFromFormFactory(entry.getValue().getElement()); + Collection formTypeFromFormFactory = FormFieldResolver.getFormTypeFromFormFactory(element); if (formTypeFromFormFactory.size() > 0) { typeText = StringUtils.stripStart(formTypeFromFormFactory.iterator().next().getFQN(), "\\"); } for (String s : new String[]{"form_start", "form_rest", "form_end", "form_errors"}) { - LookupElementBuilder element = LookupElementBuilder.create(s + "(" + entry.getKey() + ")") + LookupElementBuilder lookupElement = LookupElementBuilder.create(s + "(" + entry.getKey() + ")") .withTypeText(typeText) .withIcon(Symfony2Icons.FORM_TYPE); - resultSet.addElement(element); + resultSet.addElement(lookupElement); } } } 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 c485343ac..897e33537 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 @@ -155,9 +155,18 @@ public static void visitFormReferencesFields(PsiElement formReference, @NotNull return; } - for (TwigTypeContainer twigTypeContainer : getTwigTypeContainer(method, phpClass)) { - consumer.accept(twigTypeContainer); - } + visitFormReferencesFields(phpClass, consumer); + } + } + + public static void visitFormReferencesFields(@NotNull PhpClass phpClass, @NotNull Consumer consumer) { + Method method = phpClass.findMethodByName("buildForm"); + if(method == null) { + return; + } + + for (TwigTypeContainer twigTypeContainer : getTwigTypeContainer(method, phpClass)) { + consumer.accept(twigTypeContainer); } } } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/twig/action/TwigFormFieldGenerator.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/twig/action/TwigFormFieldGenerator.java new file mode 100644 index 000000000..a047ddd02 --- /dev/null +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/twig/action/TwigFormFieldGenerator.java @@ -0,0 +1,110 @@ +package fr.adrienbrault.idea.symfony2plugin.twig.action; + +import com.intellij.codeInsight.CodeInsightActionHandler; +import com.intellij.codeInsight.actions.CodeInsightAction; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.impl.source.html.HtmlFileImpl; +import com.jetbrains.php.PhpIndex; +import com.jetbrains.php.completion.insert.PhpInsertHandlerUtil; +import com.jetbrains.php.lang.psi.elements.PhpClass; +import com.jetbrains.php.lang.psi.resolve.types.PhpType; +import com.jetbrains.twig.TwigFile; +import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; +import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigTypeResolveUtil; +import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil; +import fr.adrienbrault.idea.symfony2plugin.templating.variable.dict.PsiVariable; +import fr.adrienbrault.idea.symfony2plugin.templating.variable.resolver.FormFieldResolver; +import org.apache.commons.lang.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +/** + * @author Daniel Espendiller + */ +public class TwigFormFieldGenerator extends CodeInsightAction { + @Override + protected boolean isValidForFile(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { + return Symfony2ProjectComponent.isEnabled(project) && ( + file instanceof TwigFile + || (file instanceof HtmlFileImpl && file.getName().toLowerCase().endsWith(".twig")) + || TwigUtil.getInjectedTwigElement(file, editor) != null + ); + } + + protected CodeInsightActionHandler getHandler() { + return new TwigFormFieldGenerator.MyCodeInsightActionHandler(); + } + + private static class MyCodeInsightActionHandler implements CodeInsightActionHandler { + @Override + public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile psiFile) { + PsiElement psiElement = TwigUtil.getInjectedTwigElement(psiFile, editor); + if (psiElement == null) { + return; + } + + Collection phpClasses = new ArrayList<>(); + + for (Map.Entry entry : TwigTypeResolveUtil.collectScopeVariables(psiElement).entrySet()) { + PhpType phpType = PhpIndex.getInstance(project).completeType(project, PhpType.from(entry.getValue().getTypes().toArray(new String[0])), new HashSet<>()); + if (phpType.types().noneMatch(s -> s.equals("\\Symfony\\Component\\Form\\FormView"))) { + continue; + } + + PsiElement element = entry.getValue().getElement(); + if (element == null) { + continue; + } + + for (PhpClass phpClass : FormFieldResolver.getFormTypeFromFormFactory(element)) { + phpClasses.add(new JBFormFieldItem(entry.getKey(), phpClass)); + } + } + + if (phpClasses.size() == 1) { + extracted(project, editor, phpClasses.stream().iterator().next()); + return; + } + + JBPopupFactory.getInstance().createPopupChooserBuilder(new ArrayList<>(phpClasses)) + .setTitle("Symfony: Select FormType") + .setItemChosenCallback(strings -> WriteCommandAction.runWriteCommandAction(project, "", null, () -> { + extracted(project, editor, strings); + })) + .createPopup() + .showInBestPositionFor(editor); + } + + private static void extracted(@NotNull Project project, @NotNull Editor editor, @NotNull TwigFormFieldGenerator.JBFormFieldItem next) { + Collection fields = new HashSet<>(); + FormFieldResolver.visitFormReferencesFields(next.phpClass(), twigTypeContainer -> fields.add(twigTypeContainer.getStringElement())); + + JBPopupFactory.getInstance().createPopupChooserBuilder(new ArrayList<>(fields)) + .setTitle(String.format("Symfony: Select Form Fields \"%s\"", StringUtils.abbreviate(next.phpClass.getName(), 20))) + .setItemsChosenCallback(strings -> WriteCommandAction.runWriteCommandAction(project, "", null, () -> { + StringBuilder s = new StringBuilder(); + + for (String string : strings) { + s.append(String.format("{{ form_widget(%s.%s) }}\n", next.key, string)); + } + + PhpInsertHandlerUtil.insertStringAtCaret(editor, s.toString()); + })) + .createPopup() + .showInBestPositionFor(editor); + } + } + + public record JBFormFieldItem(@NotNull String key, @NotNull PhpClass phpClass) { + @Override + public String toString() { + return key + " => " + phpClass.getName(); + } + } +} diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 23a8defa1..ceb854c6c 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -756,6 +756,10 @@ + + + +