-
-
Notifications
You must be signed in to change notification settings - Fork 137
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2193 from Haehnchen/feature/twig-attribute-html
support "ExposeInTemplate" variables for html attributes of "<twig:>" prefix
- Loading branch information
Showing
6 changed files
with
171 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,15 +12,18 @@ | |
import com.intellij.psi.xml.XmlTokenType; | ||
import com.jetbrains.php.lang.psi.elements.Field; | ||
import com.jetbrains.php.lang.psi.elements.PhpClass; | ||
import com.jetbrains.php.lang.psi.elements.PhpNamedElement; | ||
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; | ||
import fr.adrienbrault.idea.symfony2plugin.routing.Route; | ||
import fr.adrienbrault.idea.symfony2plugin.routing.RouteHelper; | ||
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigHtmlCompletionUtil; | ||
import fr.adrienbrault.idea.symfony2plugin.util.UxUtil; | ||
import kotlin.Pair; | ||
import org.apache.commons.lang.StringUtils; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.function.Consumer; | ||
|
||
/** | ||
* @author Daniel Espendiller <[email protected]> | ||
|
@@ -87,10 +90,11 @@ public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int offset, | |
Project project = psiElement.getProject(); | ||
|
||
for (PhpClass phpClass : UxUtil.getTwigComponentNameTargets(project, htmlTag.getName().substring(5))) { | ||
Field fieldByName = phpClass.findFieldByName(StringUtils.stripStart(text, ":"), false); | ||
if (fieldByName != null) { | ||
targets.add(fieldByName); | ||
} | ||
UxUtil.visitComponentVariables(phpClass, pair -> { | ||
if (pair.getFirst().equals(StringUtils.stripStart(text, ":"))) { | ||
targets.add(pair.getSecond()); | ||
} | ||
}); | ||
} | ||
}; | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,18 +1,14 @@ | ||
package fr.adrienbrault.idea.symfony2plugin.twig.variable.collector; | ||
|
||
import com.intellij.psi.PsiFile; | ||
import com.jetbrains.php.lang.psi.elements.*; | ||
import com.jetbrains.php.lang.psi.elements.PhpClass; | ||
import com.jetbrains.twig.TwigFile; | ||
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigTypeResolveUtil; | ||
import fr.adrienbrault.idea.symfony2plugin.templating.variable.TwigFileVariableCollector; | ||
import fr.adrienbrault.idea.symfony2plugin.templating.variable.TwigFileVariableCollectorParameter; | ||
import fr.adrienbrault.idea.symfony2plugin.templating.variable.dict.PsiVariable; | ||
import fr.adrienbrault.idea.symfony2plugin.util.PhpPsiAttributesUtil; | ||
import fr.adrienbrault.idea.symfony2plugin.util.UxUtil; | ||
import org.jetbrains.annotations.NotNull; | ||
|
||
import java.util.Collection; | ||
import java.util.HashSet; | ||
import java.util.Map; | ||
|
||
/** | ||
|
@@ -22,72 +18,14 @@ | |
* @author Daniel Espendiller <[email protected]> | ||
*/ | ||
public class UxComponentVariableCollector implements TwigFileVariableCollector { | ||
private static final String ATTRIBUTE_EXPOSE_IN_TEMPLATE = "\\Symfony\\UX\\TwigComponent\\Attribute\\ExposeInTemplate"; | ||
|
||
public void collectPsiVariables(@NotNull TwigFileVariableCollectorParameter parameter, @NotNull Map<String, PsiVariable> variables) { | ||
PsiFile psiFile = parameter.getElement().getContainingFile(); | ||
if (!(psiFile instanceof TwigFile)) { | ||
return; | ||
} | ||
|
||
for (PhpClass phpClass : UxUtil.getComponentClassesForTemplateFile(parameter.getProject(), psiFile)) { | ||
for (Field field : phpClass.getFields()) { | ||
if (field.getModifier().isPublic()) { | ||
for (String name : getExposeName(field)) { | ||
variables.put(name, new PsiVariable(field.getType().getTypes(), field)); | ||
} | ||
} | ||
|
||
if (field.getModifier().isPrivate() && field.getAttributes(ATTRIBUTE_EXPOSE_IN_TEMPLATE).size() > 0) { | ||
for (String name : getExposeName(field)) { | ||
variables.put(name, new PsiVariable(field.getType().getTypes(), field)); | ||
} | ||
} | ||
} | ||
|
||
for (Method method : phpClass.getMethods()) { | ||
if (method.getAccess().isPublic() && method.getAttributes(ATTRIBUTE_EXPOSE_IN_TEMPLATE).size() > 0) { | ||
for (String name : getExposeName(method)) { | ||
variables.put(name, new PsiVariable(method.getType().getTypes(), method)); | ||
} | ||
} | ||
} | ||
UxUtil.visitComponentVariables(phpClass, pair -> variables.put(pair.getFirst(), new PsiVariable(pair.getSecond().getType().getTypes(), pair.getSecond()))); | ||
} | ||
} | ||
|
||
private Collection<String> getExposeName(@NotNull PhpAttributesOwner phpAttributesOwner) { | ||
Collection<String> names = new HashSet<>(); | ||
|
||
// public state | ||
Collection<@NotNull PhpAttribute> attributes = phpAttributesOwner.getAttributes(ATTRIBUTE_EXPOSE_IN_TEMPLATE); | ||
if (attributes.size() == 0) { | ||
String name = phpAttributesOwner.getName(); | ||
|
||
if (phpAttributesOwner instanceof Method method) { | ||
names.add(TwigTypeResolveUtil.getPropertyShortcutMethodName(method)); | ||
} else { | ||
names.add(name); | ||
} | ||
|
||
return names; | ||
} | ||
|
||
// attributes given | ||
for (PhpAttribute attribute : attributes) { | ||
String name = PhpPsiAttributesUtil.getAttributeValueByNameAsStringWithDefaultParameterFallback(attribute, "name"); | ||
if (name != null && !name.isBlank()) { | ||
names.add(name); | ||
break; | ||
} | ||
|
||
if (phpAttributesOwner instanceof Method method) { | ||
// public function getActions(): array // available as `{{ actions }}` | ||
names.add(TwigTypeResolveUtil.getPropertyShortcutMethodName(method)); | ||
} else { | ||
names.add(name); | ||
} | ||
} | ||
|
||
return names; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,36 @@ | ||
package fr.adrienbrault.idea.symfony2plugin.tests.util; | ||
|
||
import com.jetbrains.php.PhpIndex; | ||
import com.jetbrains.php.lang.PhpFileType; | ||
import com.jetbrains.php.lang.psi.PhpFile; | ||
import com.jetbrains.php.lang.psi.PhpPsiElementFactory; | ||
import com.jetbrains.php.lang.psi.elements.Field; | ||
import com.jetbrains.php.lang.psi.elements.Method; | ||
import com.jetbrains.php.lang.psi.elements.PhpClass; | ||
import com.jetbrains.php.lang.psi.elements.PhpNamedElement; | ||
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase; | ||
import fr.adrienbrault.idea.symfony2plugin.util.UxUtil; | ||
import kotlin.Pair; | ||
|
||
import java.util.Collection; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.function.Consumer; | ||
|
||
/** | ||
* @author Daniel Espendiller <[email protected]> | ||
*/ | ||
public class UxUtilTest extends SymfonyLightCodeInsightFixtureTestCase { | ||
|
||
public void setUp() throws Exception { | ||
super.setUp(); | ||
myFixture.configureFromExistingVirtualFile(myFixture.copyFileToProject("UxUtil.php")); | ||
} | ||
|
||
public String getTestDataPath() { | ||
return "src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/util/fixtures"; | ||
} | ||
|
||
public void testVisitAsTwigComponent() { | ||
PhpFile phpFile = (PhpFile) PhpPsiElementFactory.createPsiFileFromText(getProject(), "<?php\n" + | ||
"namespace App\\Components;\n" + | ||
|
@@ -67,4 +83,21 @@ public void testGetTwigComponentNameTarget() { | |
UxUtil.getTwigComponentNameTargets(getProject(), "Alert").iterator().next().getFQN() | ||
); | ||
} | ||
|
||
public void testVisitComponentVariables() { | ||
Collection<PhpClass> anyByFQN = PhpIndex.getInstance(getProject()).getAnyByFQN("\\App\\Alert"); | ||
|
||
Map<String, PhpNamedElement> map = new HashMap<>(); | ||
UxUtil.visitComponentVariables(anyByFQN.iterator().next(), pair -> map.put(pair.getFirst(), pair.getSecond())); | ||
|
||
assertTrue(map.get("message") instanceof Field); | ||
assertTrue(map.get("ico") instanceof Field); | ||
assertTrue(map.get("dismissable") instanceof Method); | ||
assertTrue(map.get("actions") instanceof Method); | ||
assertTrue(map.get("alert_type") instanceof Field);assertTrue(map.get("alert_type") instanceof Field); | ||
|
||
assertFalse(map.containsKey("notPublicField")); | ||
assertFalse(map.containsKey("notPrivateMethod")); | ||
assertFalse(map.containsKey("notExposedPublicMethod")); | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/util/fixtures/UxUtil.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
<?php | ||
|
||
namespace App | ||
{ | ||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent; | ||
use Symfony\UX\TwigComponent\Attribute\ExposeInTemplate; | ||
|
||
#[AsTwigComponent] | ||
class Alert | ||
{ | ||
#[ExposeInTemplate] | ||
private string $message; // available as `{{ message }}` in the template | ||
|
||
#[ExposeInTemplate('alert_type')] | ||
private string $type = 'success'; // available as `{{ alert_type }}` in the template | ||
|
||
#[ExposeInTemplate(name: 'ico', getter: 'fetchIcon')] | ||
private string $icon = 'ico-warning'; // available as `{{ ico }}` in the template using `fetchIcon()` as the getter | ||
|
||
private string $notPublicField = 'test'; | ||
|
||
/** | ||
* Required to access $this->message | ||
*/ | ||
public function getMessage(): string | ||
{ | ||
return $this->message; | ||
} | ||
|
||
/** | ||
* Required to access $this->type | ||
*/ | ||
public function getType(): string | ||
{ | ||
return $this->type; | ||
} | ||
|
||
/** | ||
* Required to access $this->icon | ||
*/ | ||
public function fetchIcon(): string | ||
{ | ||
return $this->icon; | ||
} | ||
|
||
#[ExposeInTemplate] | ||
public function getActions(): array // available as `{{ actions }}` in the template | ||
{ | ||
} | ||
|
||
#[ExposeInTemplate('dismissable')] | ||
public function canBeDismissed(): bool // available as `{{ dismissable }}` in the template | ||
{ | ||
} | ||
|
||
private function notPrivateMethod(): array {} | ||
public function notExposedPublicMethod(): array {} | ||
} | ||
|
||
} |