Skip to content

Commit

Permalink
#1984 support parameters inside "Autowire" attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
Haehnchen committed Jul 22, 2022
1 parent a91c959 commit 01ab07f
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -26,6 +28,8 @@
*/
public class DicGotoCompletionRegistrar implements GotoCompletionRegistrar {

public static final String AUTOWIRE_ATTRIBUTE_CLASS = "\\Symfony\\Component\\DependencyInjection\\Attribute\\Autowire";

@Override
public void register(@NotNull GotoCompletionRegistrarParameter registrar) {
// getParameter('FOO')
Expand Down Expand Up @@ -65,6 +69,30 @@ public void register(@NotNull GotoCompletionRegistrarParameter registrar) {
return new ParameterContributor((StringLiteralExpression) context);
}
);

// #[Autowire('<caret>')]
// #[Autowire(value: '<caret>')]
registrar.register(
PlatformPatterns.or(
PhpElementsUtil.getFirstAttributeStringPattern(AUTOWIRE_ATTRIBUTE_CLASS),
PhpElementsUtil.getAttributeNamedArgumentStringPattern(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, AUTOWIRE_ATTRIBUTE_CLASS)) {
return new ParameterContributor((StringLiteralExpression) context);
}
}

return null;
}
);
}

private static class ParameterContributor extends GotoCompletionProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,74 @@ public boolean accepts(@NotNull FunctionReference functionReference, ProcessingC
.withLanguage(PhpLanguage.INSTANCE);
}

private static final PatternCondition<StringLiteralExpression> 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<PsiElement> 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<PsiElement> 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<PsiElement> {
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('<caret>')
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,73 @@ public void testParameterContributorFor() {
PlatformPatterns.psiElement()
);
}

public void testParameterContributorForDefaultAttribute() {
assertCompletionContains(PhpFileType.INSTANCE, "<?php\n" +
"use Symfony\\Component\\DependencyInjection\\Attribute\\Autowire;\n" +
"\n" +
"class MyService\n" +
"{\n" +
" public function __construct(\n" +
" #[Autowire('<caret>')]\n" +
" private $parameter2" +
" ) {}\n" +
"}",
"foo"
);

assertCompletionContains(PhpFileType.INSTANCE, "<?php\n" +
"use Symfony\\Component\\DependencyInjection\\Attribute\\Autowire;\n" +
"\n" +
"class MyService\n" +
"{\n" +
" public function __construct(\n" +
" #[Autowire(\"<caret>\")]\n" +
" private $parameter2" +
" ) {}\n" +
"}",
"foo"
);

assertNavigationMatch(PhpFileType.INSTANCE, "<?php\n" +
"use Symfony\\Component\\DependencyInjection\\Attribute\\Autowire;\n" +
"\n" +
"class MyService\n" +
"{\n" +
" public function __construct(\n" +
" #[Autowire('fo<caret>o')]\n" +
" private $parameter2" +
" ) {}\n" +
"}",
PlatformPatterns.psiElement()
);
}

public void testParameterContributorForNamedAttribute() {
assertCompletionContains(PhpFileType.INSTANCE, "<?php\n" +
"use Symfony\\Component\\DependencyInjection\\Attribute\\Autowire;\n" +
"\n" +
"class MyService\n" +
"{\n" +
" public function __construct(\n" +
" #[Autowire(value: '<caret>')]\n" +
" private $parameter2" +
" ) {}\n" +
"}",
"foo"
);

assertNavigationMatch(PhpFileType.INSTANCE, "<?php\n" +
"use Symfony\\Component\\DependencyInjection\\Attribute\\Autowire;\n" +
"\n" +
"class MyService\n" +
"{\n" +
" public function __construct(\n" +
" #[Autowire(value: 'fo<caret>o')]\n" +
" private $parameter2" +
" ) {}\n" +
"}",
PlatformPatterns.psiElement()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 01ab07f

Please sign in to comment.