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 1bd44963b..2fa6c8f90 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 @@ -74,6 +74,7 @@ public class ServiceContainerUtil { public static final String TAGGED_ITERATOR_ATTRIBUTE_CLASS = "\\Symfony\\Component\\DependencyInjection\\Attribute\\TaggedIterator"; public static final String TAGGED_LOCATOR_ATTRIBUTE_CLASS = "\\Symfony\\Component\\DependencyInjection\\Attribute\\TaggedLocator"; public static final String DECORATOR_ATTRIBUTE_CLASS = "\\Symfony\\Component\\DependencyInjection\\Attribute\\AsDecorator"; + public static final String AUTOCONFIGURE_ATTRIBUTE_CLASS = "\\Symfony\\Component\\DependencyInjection\\Attribute\\Autoconfigure"; @NotNull public static Collection getServicesInFile(@NotNull PsiFile psiFile) { 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 65a3d9838..9de2332db 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 @@ -89,14 +89,42 @@ public void register(@NotNull GotoCompletionRegistrarParameter registrar) { } ); - // #[TaggedIterator('app.handler')] iterable $handlers - // #[TaggedLocator('app.handler')] ContainerInterface $handlers registrar.register( PlatformPatterns.or( + // #[TaggedIterator('app.handler')] iterable $handlers + // #[TaggedIterator(tag: 'app.handler')] iterable $handlers PhpElementsUtil.getFirstAttributeStringPattern(ServiceContainerUtil.TAGGED_ITERATOR_ATTRIBUTE_CLASS), PhpElementsUtil.getAttributeNamedArgumentStringPattern(ServiceContainerUtil.TAGGED_ITERATOR_ATTRIBUTE_CLASS, "tag"), + + // #[TaggedLocator('app.handler')] ContainerInterface $handlers + // #[TaggedLocator(tag: 'app.handler')] ContainerInterface $handlers PhpElementsUtil.getFirstAttributeStringPattern(ServiceContainerUtil.TAGGED_LOCATOR_ATTRIBUTE_CLASS), - PhpElementsUtil.getAttributeNamedArgumentStringPattern(ServiceContainerUtil.TAGGED_LOCATOR_ATTRIBUTE_CLASS, "tag") + PhpElementsUtil.getAttributeNamedArgumentStringPattern(ServiceContainerUtil.TAGGED_LOCATOR_ATTRIBUTE_CLASS, "tag"), + + // #[Autoconfigure(['app.some_tag'])] + // #[Autoconfigure(tags: ['app.some_tag'])] + PhpElementsUtil.getFirstAttributeArrayStringPattern(ServiceContainerUtil.AUTOCONFIGURE_ATTRIBUTE_CLASS), + PhpElementsUtil.getAttributeNamedArgumentArrayStringPattern(ServiceContainerUtil.AUTOCONFIGURE_ATTRIBUTE_CLASS, "tags") + ), psiElement -> { + PsiElement context = psiElement.getContext(); + if (!(context instanceof StringLiteralExpression)) { + return null; + } + + PhpAttribute phpAttribute = PsiTreeUtil.getParentOfType(context, PhpAttribute.class); + if (phpAttribute != null) { + return new TaggedIteratorContributor((StringLiteralExpression) context); + } + + return null; + } + ); + + // #[Autoconfigure(tags: ['app.some_tag'])] + // #[Autoconfigure(['app.some_tag'])] + registrar.register( + PlatformPatterns.or( + ), psiElement -> { PsiElement context = psiElement.getContext(); if (!(context instanceof StringLiteralExpression)) { 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 f32b73848..49ccd64ec 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/PhpElementsUtil.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/PhpElementsUtil.java @@ -382,9 +382,9 @@ public boolean accepts(@NotNull FunctionReference functionReference, ProcessingC .withLanguage(PhpLanguage.INSTANCE); } - private static final PatternCondition EMPTY_PREVIOUS_LEAF = new PatternCondition<>("previous leaf empty") { + private static final PatternCondition EMPTY_PREVIOUS_LEAF = new PatternCondition<>("previous leaf empty") { @Override - public boolean accepts(@NotNull StringLiteralExpression stringLiteralExpression, ProcessingContext context) { + public boolean accepts(@NotNull PsiElement stringLiteralExpression, ProcessingContext context) { return stringLiteralExpression.getPrevSibling() == null; } }; @@ -408,6 +408,26 @@ public static PsiElementPattern.Capture getFirstAttributeStringPatte ); } + /** + * #[Security("is_granted(['POST_SHOW'])")] + */ + @NotNull + public static PsiElementPattern.Capture getFirstAttributeArrayStringPattern(@NotNull String clazz) { + return PlatformPatterns.psiElement().withElementType(PlatformPatterns.elementType().or( + PhpTokenTypes.STRING_LITERAL_SINGLE_QUOTE, + PhpTokenTypes.STRING_LITERAL + )) + .withParent(PlatformPatterns.psiElement(StringLiteralExpression.class) + .withParent(PlatformPatterns.psiElement(PhpElementTypes.ARRAY_VALUE).withParent( + PlatformPatterns.psiElement(ArrayCreationExpression.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')")] */ @@ -433,6 +453,33 @@ public static PsiElementPattern.Capture getAttributeNamedArgumentStr ); } + /** + * #[Security(tags: ['foobar']])] + */ + @NotNull + public static PsiElementPattern.Capture getAttributeNamedArgumentArrayStringPattern(@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) + .withParent(PlatformPatterns.psiElement(PhpElementTypes.ARRAY_VALUE).withParent( + PlatformPatterns.psiElement(ArrayCreationExpression.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 */ 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 999d2ea88..a921d1bda 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 @@ -258,4 +258,34 @@ public void testServiceContributorDecoratesAttribute() { PlatformPatterns.psiElement() ); } + + public void testTagContributorForAutoconfigureTagsAttribute() { + assertCompletionContains(PhpFileType.INSTANCE, "'])]\n" + + "class HandlerCollection {}", + "yaml_type_tag" + ); + + assertNavigationMatch(PhpFileType.INSTANCE, "type_tag'])]\n" + + "class HandlerCollection {}", + PlatformPatterns.psiElement() + ); + + assertCompletionContains(PhpFileType.INSTANCE, "'])]\n" + + "class HandlerCollection {}", + "yaml_type_tag" + ); + + assertNavigationMatch(PhpFileType.INSTANCE, "type_tag'])]\n" + + "class HandlerCollection {}", + 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 1632ba0d9..9d21770fc 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 @@ -57,6 +57,22 @@ public function __construct( ) { } } + + class Autoconfigure + { + public function __construct( + public ?array $tags = null, + public ?array $calls = null, + public ?array $bind = null, + public bool|string|null $lazy = null, + public ?bool $public = null, + public ?bool $shared = null, + public ?bool $autowire = null, + public ?array $properties = null, + public array|string|null $configurator = null, + ) { + } + } } namespace