From a73032f72c9c6b9ad79c847cbb2c15c574c11bea Mon Sep 17 00:00:00 2001 From: Daniel Espendiller Date: Fri, 22 Jul 2022 20:01:51 +0200 Subject: [PATCH] #1984 support parameters inside "Autowire" attribute --- .../container/util/ServiceContainerUtil.java | 2 + .../registrar/DicGotoCompletionRegistrar.java | 28 +++++++- .../symfony2plugin/util/PhpElementsUtil.java | 68 ++++++++++++++++++ .../DicGotoCompletionRegistrarTest.java | 69 +++++++++++++++++++ .../tests/dic/registrar/fixtures/classes.php | 17 +++++ 5 files changed, 183 insertions(+), 1 deletion(-) diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/container/util/ServiceContainerUtil.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/container/util/ServiceContainerUtil.java index 750c80f7b..43a9d051b 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/container/util/ServiceContainerUtil.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/container/util/ServiceContainerUtil.java @@ -69,6 +69,8 @@ public class ServiceContainerUtil { "debug", "default", "abstract", "inner", "chain", "decorate", "delegat" }; + public static final String AUTOWIRE_ATTRIBUTE_CLASS = "\\Symfony\\Component\\DependencyInjection\\Attribute\\Autowire"; + @NotNull public static Collection getServicesInFile(@NotNull PsiFile psiFile) { final Collection services = new ArrayList<>(); diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/registrar/DicGotoCompletionRegistrar.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/registrar/DicGotoCompletionRegistrar.java index 8eae00863..28a8e3696 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/registrar/DicGotoCompletionRegistrar.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/registrar/DicGotoCompletionRegistrar.java @@ -3,6 +3,8 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.jetbrains.php.lang.psi.elements.PhpAttribute; import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProvider; import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrar; @@ -10,6 +12,7 @@ import fr.adrienbrault.idea.symfony2plugin.codeInsight.utils.GotoCompletionUtil; import fr.adrienbrault.idea.symfony2plugin.config.component.ParameterLookupElement; import fr.adrienbrault.idea.symfony2plugin.dic.ContainerParameter; +import fr.adrienbrault.idea.symfony2plugin.dic.container.util.ServiceContainerUtil; import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver; import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher; import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil; @@ -25,7 +28,6 @@ * @author Daniel Espendiller */ public class DicGotoCompletionRegistrar implements GotoCompletionRegistrar { - @Override public void register(@NotNull GotoCompletionRegistrarParameter registrar) { // getParameter('FOO') @@ -65,6 +67,30 @@ public void register(@NotNull GotoCompletionRegistrarParameter registrar) { return new ParameterContributor((StringLiteralExpression) context); } ); + + // #[Autowire('')] + // #[Autowire(value: '')] + registrar.register( + PlatformPatterns.or( + PhpElementsUtil.getFirstAttributeStringPattern(ServiceContainerUtil.AUTOWIRE_ATTRIBUTE_CLASS), + PhpElementsUtil.getAttributeNamedArgumentStringPattern(ServiceContainerUtil.AUTOWIRE_ATTRIBUTE_CLASS, "value") + ), psiElement -> { + PsiElement context = psiElement.getContext(); + if (!(context instanceof StringLiteralExpression)) { + return null; + } + + PhpAttribute phpAttribute = PsiTreeUtil.getParentOfType(context, PhpAttribute.class); + if (phpAttribute != null) { + String fqn = phpAttribute.getFQN(); + if (fqn != null && PhpElementsUtil.isInstanceOf(psiElement.getProject(), fqn, ServiceContainerUtil.AUTOWIRE_ATTRIBUTE_CLASS)) { + return new ParameterContributor((StringLiteralExpression) context); + } + } + + return null; + } + ); } private static class ParameterContributor extends GotoCompletionProvider { diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/PhpElementsUtil.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/PhpElementsUtil.java index 5bf49ea87..f32b73848 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/PhpElementsUtil.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/PhpElementsUtil.java @@ -382,6 +382,74 @@ public boolean accepts(@NotNull FunctionReference functionReference, ProcessingC .withLanguage(PhpLanguage.INSTANCE); } + private static final PatternCondition EMPTY_PREVIOUS_LEAF = new PatternCondition<>("previous leaf empty") { + @Override + public boolean accepts(@NotNull StringLiteralExpression stringLiteralExpression, ProcessingContext context) { + return stringLiteralExpression.getPrevSibling() == null; + } + }; + + /** + * #[Security("is_granted('POST_SHOW')")] + */ + @NotNull + public static PsiElementPattern.Capture getFirstAttributeStringPattern(@NotNull String clazz) { + return PlatformPatterns.psiElement().withElementType(PlatformPatterns.elementType().or( + PhpTokenTypes.STRING_LITERAL_SINGLE_QUOTE, + PhpTokenTypes.STRING_LITERAL + )) + .withParent(PlatformPatterns.psiElement(StringLiteralExpression.class) + .with(EMPTY_PREVIOUS_LEAF) + .withParent(PlatformPatterns.psiElement(ParameterList.class) + .withParent(PlatformPatterns.psiElement(PhpAttribute.class) + .with(new AttributeInstancePatternCondition(clazz)) + ) + ) + ); + } + + /** + * #[Security(foobar: "is_granted('POST_SHOW')")] + */ + @NotNull + public static PsiElementPattern.Capture getAttributeNamedArgumentStringPattern(@NotNull String clazz, @NotNull String namedArgument) { + return PlatformPatterns.psiElement().withElementType(PlatformPatterns.elementType().or( + PhpTokenTypes.STRING_LITERAL_SINGLE_QUOTE, + PhpTokenTypes.STRING_LITERAL + )) + .withParent(PlatformPatterns.psiElement(StringLiteralExpression.class) + .afterLeafSkipping( + PlatformPatterns.psiElement(PsiWhiteSpace.class), + PlatformPatterns.psiElement(PhpTokenTypes.opCOLON).afterLeafSkipping( + PlatformPatterns.psiElement(PsiWhiteSpace.class), + PlatformPatterns.psiElement(PhpTokenTypes.IDENTIFIER).withText(namedArgument) + ) + ) + .withParent(PlatformPatterns.psiElement(ParameterList.class) + .withParent(PlatformPatterns.psiElement(PhpAttribute.class) + .with(new AttributeInstancePatternCondition(clazz)) + ) + ) + ); + } + + /** + * Check if given Attribute + */ + private static class AttributeInstancePatternCondition extends PatternCondition { + private final String clazz; + + AttributeInstancePatternCondition(@NotNull String clazz) { + super("Attribute Instance"); + this.clazz = clazz; + } + + @Override + public boolean accepts(@NotNull PsiElement psiElement, ProcessingContext processingContext) { + return clazz.equals(((PhpAttribute) psiElement).getFQN()); + } + } + /** * $foo->bar('') */ diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/dic/registrar/DicGotoCompletionRegistrarTest.java b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/dic/registrar/DicGotoCompletionRegistrarTest.java index 36cf9967a..9e8b6b059 100644 --- a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/dic/registrar/DicGotoCompletionRegistrarTest.java +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/dic/registrar/DicGotoCompletionRegistrarTest.java @@ -63,4 +63,73 @@ public void testParameterContributorFor() { PlatformPatterns.psiElement() ); } + + public void testParameterContributorForDefaultAttribute() { + assertCompletionContains(PhpFileType.INSTANCE, "')]\n" + + " private $parameter2" + + " ) {}\n" + + "}", + "foo" + ); + + assertCompletionContains(PhpFileType.INSTANCE, "\")]\n" + + " private $parameter2" + + " ) {}\n" + + "}", + "foo" + ); + + assertNavigationMatch(PhpFileType.INSTANCE, "o')]\n" + + " private $parameter2" + + " ) {}\n" + + "}", + PlatformPatterns.psiElement() + ); + } + + public void testParameterContributorForNamedAttribute() { + assertCompletionContains(PhpFileType.INSTANCE, "')]\n" + + " private $parameter2" + + " ) {}\n" + + "}", + "foo" + ); + + assertNavigationMatch(PhpFileType.INSTANCE, "o')]\n" + + " private $parameter2" + + " ) {}\n" + + "}", + PlatformPatterns.psiElement() + ); + } } diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/dic/registrar/fixtures/classes.php b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/dic/registrar/fixtures/classes.php index 3600f2ddb..e4995ea34 100644 --- a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/dic/registrar/fixtures/classes.php +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/dic/registrar/fixtures/classes.php @@ -9,6 +9,23 @@ function hasParameter($parameter); } } +namespace Symfony\Component\DependencyInjection\Attribute +{ + class Autowire + { + /** + * @param string|null $value Parameter value (ie "%kernel.project_dir%/some/path") + * @param string|null $service Service ID (ie "some.service") + * @param string|null $expression Expression (ie 'service("some.service").someMethod()') + */ + public function __construct( + string $value = null, + string $service = null, + string $expression = null, + ) {} + } +} + namespace { class Foo