Skip to content

Commit

Permalink
#1984 support service inside "Autowire" attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
Haehnchen committed Jul 23, 2022
1 parent 1cb02e8 commit de898ca
Show file tree
Hide file tree
Showing 9 changed files with 209 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.ServiceSuggestionCollector;
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.XmlCallServiceSuggestionCollector;
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.XmlConstructServiceSuggestionCollector;
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.YamlConstructServiceSuggestionCollector;
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.*;
import fr.adrienbrault.idea.symfony2plugin.dic.container.util.ServiceContainerUtil;
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
import org.jetbrains.annotations.NotNull;
Expand All @@ -28,6 +25,7 @@ public class ServiceCompletionProvider extends CompletionProvider<CompletionPara
new XmlConstructServiceSuggestionCollector(),
new YamlConstructServiceSuggestionCollector(),
new XmlCallServiceSuggestionCollector(),
new PhpAttributeServiceSuggestionCollector(),
};

public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet resultSet) {
Expand All @@ -39,7 +37,8 @@ public void addCompletions(@NotNull CompletionParameters parameters, ProcessingC
PsiElement element = parameters.getPosition();

PrioritizedLookupResult result = getLookupElements(
element, ContainerCollectionResolver.getServices(element.getProject()).values()
element,
ContainerCollectionResolver.getServices(element.getProject()).values()
);

addPrioritizedServiceLookupElements(parameters, resultSet, result);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion;

import com.intellij.psi.PsiElement;
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerService;
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.utils.ServiceSuggestionUtil;
import fr.adrienbrault.idea.symfony2plugin.dic.container.util.ServiceContainerUtil;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class PhpAttributeServiceSuggestionCollector implements ServiceSuggestionCollector {

@NotNull
public Collection<String> collect(@NotNull PsiElement psiElement, @NotNull Collection<ContainerService> serviceMap) {
return ServiceSuggestionUtil.createSuggestions(ServiceContainerUtil.getPhpAttributeConstructorTypeHint(psiElement), serviceMap);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import com.intellij.util.Consumer;
import com.intellij.util.indexing.FileBasedIndex;
import com.jetbrains.php.PhpIndex;
import com.jetbrains.php.lang.lexer.PhpTokenTypes;
import com.jetbrains.php.lang.psi.PhpFile;
import com.jetbrains.php.lang.psi.elements.*;
import com.jetbrains.php.refactoring.PhpNamespaceBraceConverter;
Expand Down Expand Up @@ -436,6 +437,58 @@ public static ServiceTypeHint getYamlConstructorTypeHint(@NotNull PsiElement psi
return getYamlConstructorTypeHint((YAMLScalar) yamlScalar, lazyServiceCollector);
}

/**
* Resolve class for attribute and its parameter index
*
* class OurClass {
* public function __construct(#[Autowire(service: 'some_service')] private $service1) {}
* public function setFoo(#[Autowire(service: 'some_service')] private $service1) {}
* }
*/
public static ServiceTypeHint getPhpAttributeConstructorTypeHint(@NotNull PsiElement psiElement) {
if (!(psiElement instanceof StringLiteralExpression)) {
return null;
}

PsiElement parameterList = psiElement.getContext();
if (!(parameterList instanceof ParameterList)) {
return null;
}

PsiElement colon = PsiTreeUtil.prevCodeLeaf(psiElement);
if (colon == null || colon.getNode().getElementType() != PhpTokenTypes.opCOLON) {
return null;
}

PsiElement argumentName = PsiTreeUtil.prevCodeLeaf(colon);
if (argumentName == null || argumentName.getNode().getElementType() != PhpTokenTypes.IDENTIFIER) {
return null;
}

// now resolve method attribute: its class and parameter index
PsiElement phpAttribute = parameterList.getParent();
if (phpAttribute instanceof PhpAttribute) {
PsiElement phpAttributesList = phpAttribute.getParent();
if (phpAttributesList instanceof PhpAttributesList) {
PsiElement parameter = phpAttributesList.getParent();
if (parameter instanceof Parameter) {
PsiElement parameterListMethod = parameter.getParent();
if (parameterListMethod instanceof ParameterList) {
PsiElement method = parameterListMethod.getParent();
if (method instanceof Method) {
ParameterBag currentParameterIndex = PsiElementUtils.getCurrentParameterIndex((Parameter) parameter);
if (currentParameterIndex != null) {
return new ServiceTypeHint((Method) method, currentParameterIndex.getIndex(), psiElement);
}
}
}
}
}
}

return null;
}

/**
* foo:
* class: Foo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
import com.intellij.psi.ResolveResult;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.php.lang.psi.elements.PhpAttribute;
import com.jetbrains.php.lang.psi.elements.PhpClass;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProvider;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrar;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrarParameter;
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.ServiceCompletionProvider;
import fr.adrienbrault.idea.symfony2plugin.dic.container.util.ServiceContainerUtil;
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher;
Expand All @@ -22,10 +24,7 @@
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.*;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
Expand Down Expand Up @@ -111,6 +110,24 @@ public void register(@NotNull GotoCompletionRegistrarParameter registrar) {
return null;
}
);

// #[Autowire(service: 'some_service')]
registrar.register(
PhpElementsUtil.getAttributeNamedArgumentStringPattern(ServiceContainerUtil.AUTOWIRE_ATTRIBUTE_CLASS, "service"),
psiElement -> {
PsiElement context = psiElement.getContext();
if (!(context instanceof StringLiteralExpression)) {
return null;
}

PhpAttribute phpAttribute = PsiTreeUtil.getParentOfType(context, PhpAttribute.class);
if (phpAttribute != null) {
return new ServiceContributor((StringLiteralExpression) context);
}

return null;
}
);
}

private static class ParameterContributor extends GotoCompletionProvider {
Expand Down Expand Up @@ -165,4 +182,30 @@ public Collection<PsiElement> getPsiTargets(PsiElement element) {
return new ArrayList<>(ServiceUtil.getTaggedClasses(getElement().getProject(), contents));
}
}

private static class ServiceContributor extends GotoCompletionProvider {
public ServiceContributor(@NotNull StringLiteralExpression element) {
super(element);
}

@Override
public @NotNull Collection<LookupElement> getLookupElements() {
return ServiceCompletionProvider.getLookupElements(this.getElement(), ContainerCollectionResolver.getServices(getProject()).values()).getLookupElements();
}

@Override
public @NotNull Collection<PsiElement> getPsiTargets(PsiElement element) {
String contents = GotoCompletionUtil.getStringLiteralValue(element);
if (contents == null) {
return Collections.emptyList();
}

PhpClass phpClass = ServiceUtil.getResolvedClassDefinition(element.getProject(), contents);
if (phpClass == null) {
return Collections.emptyList();
}

return List.of(phpClass);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -166,4 +166,30 @@ public void testTagContributorForTaggedIterator() {
PlatformPatterns.psiElement()
);
}

public void testServiceContributorForNamedAttribute() {
assertCompletionContains(PhpFileType.INSTANCE, "<?php\n" +
"use Symfony\\Component\\DependencyInjection\\Attribute\\Autowire;\n" +
"\n" +
"class HandlerCollection\n" +
"{\n" +
" public function __construct(\n" +
" #[Autowire(service: '<caret>')]" +
" ) {}\n" +
"}",
"foo_bar_service"
);

assertNavigationMatch(PhpFileType.INSTANCE, "<?php\n" +
"use Symfony\\Component\\DependencyInjection\\Attribute\\Autowire;\n" +
"\n" +
"class HandlerCollection\n" +
"{\n" +
" public function __construct(\n" +
" #[Autowire(service: 'foo_bar<caret>_service')] $handlers\n" +
" ) {}\n" +
"}",
PlatformPatterns.psiElement()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ services:
Foo\Bar:
tags:
- { name: yaml_type_tag }

foo_bar_service:
class: Foo\Bar
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package fr.adrienbrault.idea.symfony2plugin.tests.dic.suggestion;

import com.jetbrains.php.lang.psi.PhpPsiElementFactory;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.PhpAttributeServiceSuggestionCollector;
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;
import org.jetbrains.annotations.Nullable;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class PhpAttributeServiceSuggestionCollectorTest extends SymfonyLightCodeInsightFixtureTestCase {
public void setUp() throws Exception {
super.setUp();
myFixture.configureFromExistingVirtualFile(myFixture.copyFileToProject("classes.php"));
myFixture.copyFileToProject("services.yml");
}

public String getTestDataPath() {
return "src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/dic/suggestion/fixtures";
}

public void testParameterContributor() {
@Nullable StringLiteralExpression stringLiteralExpression = PhpPsiElementFactory.createFromText(getProject(), StringLiteralExpression.class,
"<?php\n" +
"use Symfony\\Component\\DependencyInjection\\Attribute\\Autowire;\n" +
"\n" +
"class HandlerCollection\n" +
"{\n" +
" public function __construct(\n" +
" #[Autowire(service: '<caret>')] \\Foo\\Bar $test\n" +
" ) {}\n" +
"}"
);

assert stringLiteralExpression != null;

PhpAttributeServiceSuggestionCollector collector = new PhpAttributeServiceSuggestionCollector();
assertContainsElements(
collector.collect(stringLiteralExpression, ContainerCollectionResolver.getServices(getProject()).values()),
"foo_bar_service"
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Foo
{
class Bar
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
services:
foo_bar_service:
class: Foo\Bar

0 comments on commit de898ca

Please sign in to comment.