From cda1f6188a1f48c7524e7aa0171c0d05ee68009e Mon Sep 17 00:00:00 2001 From: Daniel Espendiller Date: Tue, 30 Oct 2018 19:30:32 +0100 Subject: [PATCH] Named arguments should be clickable #1240 --- .../yaml/YamlGoToDeclarationHandler.java | 20 ++++++++++ .../container/util/ServiceContainerUtil.java | 37 +++++++++++++++++++ .../symfony2plugin/util/PhpElementsUtil.java | 15 ++++++++ .../yaml/YamlGoToDeclarationHandlerTest.java | 11 ++++++ 4 files changed, 83 insertions(+) diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/yaml/YamlGoToDeclarationHandler.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/yaml/YamlGoToDeclarationHandler.java index 03c266845..e2804c730 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/yaml/YamlGoToDeclarationHandler.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/yaml/YamlGoToDeclarationHandler.java @@ -11,6 +11,7 @@ import com.intellij.psi.tree.IElementType; import com.jetbrains.php.PhpIndex; import com.jetbrains.php.lang.psi.elements.Method; +import com.jetbrains.php.lang.psi.elements.Parameter; import com.jetbrains.php.lang.psi.elements.PhpClass; import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; import fr.adrienbrault.idea.symfony2plugin.config.EventDispatcherSubscriberUtil; @@ -88,6 +89,14 @@ public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int i, Edit } } + if (elementType == YAMLTokenTypes.SCALAR_KEY) { + String psiText = PsiElementUtils.getText(psiElement); + + if(psiText.startsWith("$")) { + targets.addAll(namedArgumentGoto(psiElement)) ; + } + } + // yaml Plugin BC: "!php/const:" is a tag if(elementType == YAMLTokenTypes.TAG) { String psiText = PsiElementUtils.getText(psiElement); @@ -167,6 +176,17 @@ public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int i, Edit return targets.toArray(new PsiElement[targets.size()]); } + private Collection namedArgumentGoto(PsiElement psiElement) { + Collection psiElements = new HashSet<>(); + + Parameter yamlNamedArgument = ServiceContainerUtil.getYamlNamedArgument(psiElement, new ContainerCollectionResolver.LazyServiceCollector(psiElement.getProject())); + if (yamlNamedArgument != null) { + psiElements.add(yamlNamedArgument); + } + + return psiElements; + } + private boolean hasNewConst(@NotNull PsiElement psiElement) { PsiElement prevSibling = psiElement.getPrevSibling(); while (prevSibling != null) { 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 f49a580f9..7f947f405 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 @@ -12,6 +12,7 @@ import com.jetbrains.php.PhpIndex; import com.jetbrains.php.lang.psi.elements.Field; import com.jetbrains.php.lang.psi.elements.Method; +import com.jetbrains.php.lang.psi.elements.Parameter; import com.jetbrains.php.lang.psi.elements.PhpClass; import fr.adrienbrault.idea.symfony2plugin.config.xml.XmlHelper; import fr.adrienbrault.idea.symfony2plugin.dic.attribute.value.AttributeValueInterface; @@ -366,6 +367,42 @@ public static ServiceTypeHint getYamlConstructorTypeHint(@NotNull YAMLScalar yam return null; } + /** + * arguments: ['$foobar': '@foo'] + */ + @Nullable + public static Parameter getYamlNamedArgument(@NotNull PsiElement psiElement, @NotNull ContainerCollectionResolver.LazyServiceCollector lazyServiceCollector) { + PsiElement context = psiElement.getContext(); + if(context instanceof YAMLKeyValue) { + // arguments: ['$foobar': '@foo'] + + String parameterName = ((YAMLKeyValue) context).getKeyText(); + if(parameterName.startsWith("$") && parameterName.length() > 1) { + PsiElement yamlMapping = context.getParent(); + if(yamlMapping instanceof YAMLMapping) { + PsiElement yamlKeyValue = yamlMapping.getParent(); + if(yamlKeyValue instanceof YAMLKeyValue) { + String keyText = ((YAMLKeyValue) yamlKeyValue).getKeyText(); + if(keyText.equals("arguments")) { + YAMLMapping parentMapping = ((YAMLKeyValue) yamlKeyValue).getParentMapping(); + if(parentMapping != null) { + String serviceId = getServiceClassFromServiceMapping(parentMapping); + if(StringUtils.isNotBlank(serviceId)) { + PhpClass serviceClass = ServiceUtil.getResolvedClassDefinition(psiElement.getProject(), serviceId, lazyServiceCollector); + if(serviceClass != null) { + return PhpElementsUtil.getConstructorParameterArgumentByName(serviceClass, StringUtils.stripStart(parameterName, "$")); + } + } + } + } + } + } + } + } + + return null; + } + /** * Symfony 3.3: "class" is optional; use service name for its it * 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 2c4e9d6bc..2d232d967 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/PhpElementsUtil.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/PhpElementsUtil.java @@ -1299,6 +1299,21 @@ public static Collection getTernaryExpressionConditionStrings(@NotNull T return types; } + /** + * Find argument by name in constructor parameter: __construct($foobar) + */ + @Nullable + public static Parameter getConstructorParameterArgumentByName(@NotNull PhpClass phpClass, @NotNull String argumentName) { + Method constructor = phpClass.getConstructor(); + if(constructor == null) { + return null; + } + + return Arrays.stream(constructor.getParameters()).filter( + parameter -> argumentName.equals(parameter.getName()) + ).findFirst().orElse(null); + } + /** * Find argument by name in constructor parameter: __construct($foobar) */ diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/yaml/YamlGoToDeclarationHandlerTest.java b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/yaml/YamlGoToDeclarationHandlerTest.java index d636979ba..91303165d 100644 --- a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/yaml/YamlGoToDeclarationHandlerTest.java +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/yaml/YamlGoToDeclarationHandlerTest.java @@ -4,6 +4,7 @@ import com.intellij.patterns.PsiElementPattern; import com.jetbrains.php.lang.PhpFileType; import com.jetbrains.php.lang.psi.elements.Method; +import com.jetbrains.php.lang.psi.elements.Parameter; import com.jetbrains.php.lang.psi.elements.PhpClass; import com.jetbrains.php.lang.psi.elements.StringLiteralExpression; import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase; @@ -226,6 +227,16 @@ public void testThatNavigationForControllerInvokeMethodIsAvailable() { ); } + public void testNamedArgumentsNavigationForService() { + assertNavigationMatch("services.yml", "" + + "services:\n" + + " Foo\\Bar:\n" + + " arguments:\n" + + " $i: ~\n", + PlatformPatterns.psiElement(Parameter.class) + ); + } + @NotNull private PsiElementPattern.Capture getClassPattern() { return PlatformPatterns.psiElement(PhpClass.class);