Skip to content

Commit d1c4fd7

Browse files
committed
Decouple form resolvers from PhpClass PSI elements
1 parent 6926f54 commit d1c4fd7

6 files changed

Lines changed: 127 additions & 27 deletions

File tree

src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/TwigTypeResolveUtil.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -143,22 +143,24 @@ public static Collection<TwigTypeContainer> resolveTwigMethodName(@NotNull PsiEl
143143

144144
String rootType = types.iterator().next();
145145
Collection<PsiVariable> rootVariables = getRootVariableByName(psiElement, rootType);
146-
if(types.size() == 1) {
147-
Collection<TwigTypeContainer> twigTypeContainers = TwigTypeContainer.fromCollection(psiElement.getProject(), rootVariables);
146+
if (types.size() == 1) {
147+
Project project = psiElement.getProject();
148+
Collection<TwigTypeContainer> twigTypeContainers = TwigTypeContainer.fromCollection(project, rootVariables);
148149
for(TwigTypeResolver twigTypeResolver: TWIG_TYPE_RESOLVERS) {
149-
twigTypeResolver.resolve(twigTypeContainers, twigTypeContainers, rootType, new ArrayList<>(), rootVariables);
150+
twigTypeResolver.resolve(project, twigTypeContainers, twigTypeContainers, rootType, new ArrayList<>(), rootVariables);
150151
}
151152

152153
return twigTypeContainers;
153154
}
154155

155-
Collection<TwigTypeContainer> type = TwigTypeContainer.fromCollection(psiElement.getProject(), rootVariables);
156+
Project project = psiElement.getProject();
157+
Collection<TwigTypeContainer> type = TwigTypeContainer.fromCollection(project, rootVariables);
156158
Collection<List<TwigTypeContainer>> previousElements = new ArrayList<>();
157159
previousElements.add(new ArrayList<>(type));
158160

159161
String[] typeNames = types.toArray(new String[0]);
160162
for (int i = 1; i <= typeNames.length - 1; i++ ) {
161-
type = resolveTwigMethodName(type, typeNames[i], previousElements);
163+
type = resolveTwigMethodName(project, type, typeNames[i], previousElements);
162164
previousElements.add(new ArrayList<>(type));
163165

164166
// we can stop on empty list
@@ -538,7 +540,7 @@ private static Collection<PsiVariable> getRootVariableByName(@NotNull PsiElement
538540
return phpNamedElements;
539541
}
540542

541-
private static Collection<TwigTypeContainer> resolveTwigMethodName(Collection<TwigTypeContainer> previousElement, String typeName, Collection<List<TwigTypeContainer>> twigTypeContainer) {
543+
private static Collection<TwigTypeContainer> resolveTwigMethodName(@NotNull Project project, Collection<TwigTypeContainer> previousElement, String typeName, Collection<List<TwigTypeContainer>> twigTypeContainer) {
542544

543545
ArrayList<TwigTypeContainer> phpNamedElements = new ArrayList<>();
544546

@@ -565,7 +567,7 @@ private static Collection<TwigTypeContainer> resolveTwigMethodName(Collection<Tw
565567
}
566568

567569
for(TwigTypeResolver twigTypeResolver: TWIG_TYPE_RESOLVERS) {
568-
twigTypeResolver.resolve(phpNamedElements, previousElement, typeName, twigTypeContainer, null);
570+
twigTypeResolver.resolve(project, phpNamedElements, previousElement, typeName, twigTypeContainer, null);
569571
}
570572

571573
}

src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/variable/resolver/FormFieldResolver.java

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,25 +28,18 @@
2828
*/
2929
public class FormFieldResolver implements TwigTypeResolver {
3030

31-
public void resolve(Collection<TwigTypeContainer> targets, Collection<TwigTypeContainer> previousElement, String typeName, Collection<List<TwigTypeContainer>> previousElements, @Nullable Collection<PsiVariable> psiVariables) {
31+
public void resolve(@NotNull Project project, Collection<TwigTypeContainer> targets, Collection<TwigTypeContainer> previousElement, String typeName, Collection<List<TwigTypeContainer>> previousElements, @Nullable Collection<PsiVariable> psiVariables) {
3232
if (targets.isEmpty() || previousElements == null || !previousElements.isEmpty()) {
3333
return;
3434
}
3535

3636
TwigTypeContainer twigTypeContainer = targets.iterator().next();
37-
if (
38-
twigTypeContainer.getPhpNamedElement() instanceof PhpClass phpClass &&
39-
isFormView(phpClass) &&
40-
twigTypeContainer.getFormViewDataHolder() instanceof FormViewDataHolder formViewDataHolder &&
41-
!formViewDataHolder.formTypeFqns().isEmpty()
42-
) {
43-
visitFormFields(phpClass.getProject(), formViewDataHolder.formTypeFqns(), field -> targets.add(toTwigTypeContainer(field)));
37+
FormViewDataHolder formViewDataHolder = twigTypeContainer.getFormViewDataHolder();
38+
if (formViewDataHolder == null || formViewDataHolder.formTypeFqns().isEmpty()) {
39+
return;
4440
}
45-
}
4641

47-
public static boolean isFormView(@NotNull PhpClass phpClass) {
48-
return PhpElementsUtil.isInstanceOf(phpClass, "\\Symfony\\Component\\Form\\FormView") ||
49-
PhpElementsUtil.isInstanceOf(phpClass, "\\Symfony\\Component\\Form\\FormInterface"); // form view is create converting by Symfony on template render
42+
visitFormFields(project, formViewDataHolder.formTypeFqns(), field -> targets.add(toTwigTypeContainer(field)));
5043
}
5144

5245
public static boolean isFormView(@NotNull PhpType phpType) {

src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/variable/resolver/FormVarsResolver.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package fr.adrienbrault.idea.symfony2plugin.templating.variable.resolver;
22

33
import com.intellij.openapi.project.Project;
4-
import com.jetbrains.php.lang.psi.elements.PhpClass;
54
import fr.adrienbrault.idea.symfony2plugin.form.util.FormOptionsUtil;
65
import fr.adrienbrault.idea.symfony2plugin.templating.variable.TwigTypeContainer;
76
import fr.adrienbrault.idea.symfony2plugin.templating.variable.dict.PsiVariable;
7+
import fr.adrienbrault.idea.symfony2plugin.templating.variable.resolver.holder.FormViewDataHolder;
8+
import org.jetbrains.annotations.NotNull;
89
import org.jetbrains.annotations.Nullable;
910

1011
import java.util.Collection;
@@ -14,7 +15,7 @@
1415
* @author Daniel Espendiller <daniel@espendiller.net>
1516
*/
1617
public class FormVarsResolver implements TwigTypeResolver {
17-
public void resolve(Collection<TwigTypeContainer> targets, Collection<TwigTypeContainer> previousElement, String typeName, Collection<List<TwigTypeContainer>> previousElements, @Nullable Collection<PsiVariable> psiVariables) {
18+
public void resolve(@NotNull Project project, Collection<TwigTypeContainer> targets, Collection<TwigTypeContainer> previousElement, String typeName, Collection<List<TwigTypeContainer>> previousElements, @Nullable Collection<PsiVariable> psiVariables) {
1819
if(!"vars".equals(typeName) || previousElements.isEmpty()) {
1920
return;
2021
}
@@ -25,10 +26,9 @@ public void resolve(Collection<TwigTypeContainer> targets, Collection<TwigTypeCo
2526
}
2627

2728
for (TwigTypeContainer twigTypeContainer: lastTwigTypeContainer) {
28-
if (twigTypeContainer.getPhpNamedElement() instanceof PhpClass) {
29-
if (FormFieldResolver.isFormView((PhpClass) twigTypeContainer.getPhpNamedElement())) {
30-
attachVars(twigTypeContainer.getPhpNamedElement().getProject(), targets);
31-
}
29+
FormViewDataHolder formViewDataHolder = twigTypeContainer.getFormViewDataHolder();
30+
if (formViewDataHolder != null && !formViewDataHolder.formTypeFqns().isEmpty()) {
31+
attachVars(project, targets);
3232
}
3333
}
3434
}

src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/variable/resolver/TwigTypeResolver.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package fr.adrienbrault.idea.symfony2plugin.templating.variable.resolver;
22

3+
import com.intellij.openapi.project.Project;
34
import fr.adrienbrault.idea.symfony2plugin.templating.variable.TwigTypeContainer;
45
import fr.adrienbrault.idea.symfony2plugin.templating.variable.dict.PsiVariable;
6+
import org.jetbrains.annotations.NotNull;
57
import org.jetbrains.annotations.Nullable;
68

79
import java.util.Collection;
@@ -11,5 +13,5 @@
1113
* @author Daniel Espendiller <daniel@espendiller.net>
1214
*/
1315
public interface TwigTypeResolver {
14-
void resolve(Collection<TwigTypeContainer> targets, @Nullable Collection<TwigTypeContainer> previousElement, String typeName, Collection<List<TwigTypeContainer>> previousElements, @Nullable Collection<PsiVariable> psiVariables);
16+
void resolve(@NotNull Project project, Collection<TwigTypeContainer> targets, @Nullable Collection<TwigTypeContainer> previousElement, String typeName, Collection<List<TwigTypeContainer>> previousElements, @Nullable Collection<PsiVariable> psiVariables);
1517
}

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/variable/resolver/FormFieldResolverTest.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public void testResolveUsesPrimitiveFormTypeFqnsWithoutPsiElement() {
104104
assertNotNull(rootFormDataHolder);
105105
assertContainsElements(rootFormDataHolder.formTypeFqns(), "\\App\\Form\\ProductType");
106106

107-
new FormFieldResolver().resolve(targets, targets, "form", new ArrayList<>(), null);
107+
new FormFieldResolver().resolve(getProject(), targets, targets, "form", new ArrayList<>(), null);
108108

109109
TwigTypeContainer title = targets.stream()
110110
.filter(twigTypeContainer -> "title".equals(twigTypeContainer.getStringElement()))
@@ -117,6 +117,28 @@ public void testResolveUsesPrimitiveFormTypeFqnsWithoutPsiElement() {
117117
assertEquals("\\App\\Form\\ProductType", formFieldDataHolder.ownerFormTypeFqn());
118118
}
119119

120+
public void testResolveDoesNotUseFormViewClassWithoutFormViewDataHolder() {
121+
myFixture.configureByText(PhpFileType.INSTANCE, "<?php\n" +
122+
"namespace Symfony\\Component\\Form { class FormView {} interface FormTypeInterface {} interface FormBuilderInterface { public function add(); } }\n" +
123+
"namespace App\\Form {\n" +
124+
" class ProductType implements \\Symfony\\Component\\Form\\FormTypeInterface {\n" +
125+
" public function buildForm(\\Symfony\\Component\\Form\\FormBuilderInterface $builder, array $options) {\n" +
126+
" $builder->add('title');\n" +
127+
" }\n" +
128+
" }\n" +
129+
"}\n"
130+
);
131+
132+
Collection<TwigTypeContainer> targets = TwigTypeContainer.fromCollection(
133+
getProject(),
134+
Collections.singleton(new PsiVariable("\\Symfony\\Component\\Form\\FormView"))
135+
);
136+
137+
new FormFieldResolver().resolve(getProject(), targets, targets, "form", new ArrayList<>(), null);
138+
139+
assertFalse(targets.stream().anyMatch(twigTypeContainer -> "title".equals(twigTypeContainer.getStringElement())));
140+
}
141+
120142
@NotNull
121143
private MethodReference findMethodReference(@NotNull String name) {
122144
for (PsiElement psiElement : PsiTreeUtil.collectElementsOfType(myFixture.getFile(), MethodReference.class)) {
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package fr.adrienbrault.idea.symfony2plugin.tests.templating.variable.resolver;
2+
3+
import com.jetbrains.php.lang.PhpFileType;
4+
import com.jetbrains.php.lang.psi.elements.PhpClass;
5+
import fr.adrienbrault.idea.symfony2plugin.templating.variable.TwigTypeContainer;
6+
import fr.adrienbrault.idea.symfony2plugin.templating.variable.resolver.FormVarsResolver;
7+
import fr.adrienbrault.idea.symfony2plugin.templating.variable.resolver.holder.FormViewDataHolder;
8+
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;
9+
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
10+
11+
import java.util.ArrayList;
12+
import java.util.Collections;
13+
14+
/**
15+
* @author Daniel Espendiller <daniel@espendiller.net>
16+
*/
17+
public class FormVarsResolverTest extends SymfonyLightCodeInsightFixtureTestCase {
18+
public void testResolveAttachesFormVarsForFormViewDataHolder() {
19+
configureFormViewVarsFixture();
20+
21+
ArrayList<TwigTypeContainer> targets = new ArrayList<>();
22+
new FormVarsResolver().resolve(
23+
getProject(),
24+
targets,
25+
null,
26+
"vars",
27+
Collections.singletonList(Collections.singletonList(createRootFormViewContainer(true))),
28+
null
29+
);
30+
31+
assertContainsElements(
32+
targets.stream().map(TwigTypeContainer::getStringElement).toList(),
33+
"compound",
34+
"form_attr"
35+
);
36+
}
37+
38+
public void testResolveDoesNotUseFormViewClassWithoutFormViewDataHolder() {
39+
configureFormViewVarsFixture();
40+
41+
ArrayList<TwigTypeContainer> targets = new ArrayList<>();
42+
new FormVarsResolver().resolve(
43+
getProject(),
44+
targets,
45+
null,
46+
"vars",
47+
Collections.singletonList(Collections.singletonList(createRootFormViewContainer(false))),
48+
null
49+
);
50+
51+
assertEmpty(targets);
52+
}
53+
54+
private void configureFormViewVarsFixture() {
55+
myFixture.configureByText(PhpFileType.INSTANCE, "<?php\n" +
56+
"namespace Symfony\\Component\\Form {\n" +
57+
" interface FormTypeInterface { public function getName(); }\n" +
58+
" class FormView { public $vars = []; }\n" +
59+
"}\n" +
60+
"namespace Symfony\\Component\\Form\\Extension\\Core\\Type {\n" +
61+
" class FormType implements \\Symfony\\Component\\Form\\FormTypeInterface {\n" +
62+
" public function getName() { return 'form'; }\n" +
63+
" public function buildView(\\Symfony\\Component\\Form\\FormView $view, $form, array $options) {\n" +
64+
" $view->vars['form_attr'] = true;\n" +
65+
" $view->vars = array_replace($view->vars, ['compound' => true]);\n" +
66+
" }\n" +
67+
" }\n" +
68+
"}\n"
69+
);
70+
}
71+
72+
private TwigTypeContainer createRootFormViewContainer(boolean withFormViewDataHolder) {
73+
PhpClass phpClass = PhpElementsUtil.getClass(getProject(), "\\Symfony\\Component\\Form\\FormView");
74+
assertNotNull(phpClass);
75+
76+
return new TwigTypeContainer(
77+
phpClass,
78+
withFormViewDataHolder ? new FormViewDataHolder(Collections.singleton("\\App\\Form\\ProductType")) : null
79+
);
80+
}
81+
}

0 commit comments

Comments
 (0)