diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/yaml/YamlElementPatternHelper.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/yaml/YamlElementPatternHelper.java index 7c459172d..4420e190d 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/yaml/YamlElementPatternHelper.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/yaml/YamlElementPatternHelper.java @@ -679,6 +679,22 @@ public static PsiElementPattern.Capture getTagsAsSequencePattern() { ); } + /** + * services: + * _defaults: + * bind: + * $foobar: '@foobar' + */ + public static PsiElementPattern.Capture getBindArgumentPattern() { + return PlatformPatterns.psiElement().withParent( + PlatformPatterns.psiElement(YAMLKeyValue.class).withParent( + PlatformPatterns.psiElement(YAMLMapping.class).withParent( + PlatformPatterns.psiElement(YAMLKeyValue.class).withName("bind") + ) + ) + ); + } + /** * !tagged twig.extension */ 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 e2804c730..f454cd1a2 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 @@ -31,10 +31,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.yaml.YAMLTokenTypes; -import org.jetbrains.yaml.psi.YAMLKeyValue; -import org.jetbrains.yaml.psi.YAMLMapping; -import org.jetbrains.yaml.psi.YAMLScalar; -import org.jetbrains.yaml.psi.YAMLSequenceItem; +import org.jetbrains.yaml.psi.*; import java.util.*; @@ -92,8 +89,16 @@ 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)) ; + if (psiText.startsWith("$")) { + targets.addAll(namedArgumentGoto(psiElement)); + + // services: + // _defaults: + // bind: + // $foobar: '@foobar' + if (YamlElementPatternHelper.getBindArgumentPattern().accepts(psiElement)) { + targets.addAll(namedDefaultBindArgumentGoto(psiElement, psiText)); + } } } @@ -176,7 +181,29 @@ public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int i, Edit return targets.toArray(new PsiElement[targets.size()]); } - private Collection namedArgumentGoto(PsiElement psiElement) { + private Collection namedDefaultBindArgumentGoto(@NotNull PsiElement psiElement, @NotNull String parameterName) { + Collection psiElements = new HashSet<>(); + + PsiFile containingFile = psiElement.getContainingFile(); + if (containingFile instanceof YAMLFile) { + for (PhpClass phpClass : YamlHelper.getPhpClassesInYamlFile((YAMLFile) containingFile, new ContainerCollectionResolver.LazyServiceCollector(psiElement.getProject()))) { + Method constructor = phpClass.getConstructor(); + if (constructor == null) { + continue; + } + + for (Parameter parameter : constructor.getParameters()) { + if (parameter.getName().equals(parameterName.substring(1))) { + psiElements.add(parameter); + } + } + } + } + + return psiElements; + } + + private Collection namedArgumentGoto(@NotNull PsiElement psiElement) { Collection psiElements = new HashSet<>(); Parameter yamlNamedArgument = ServiceContainerUtil.getYamlNamedArgument(psiElement, new ContainerCollectionResolver.LazyServiceCollector(psiElement.getProject())); 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 7f947f405..0098f6a83 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 @@ -410,7 +410,7 @@ public static Parameter getYamlNamedArgument(@NotNull PsiElement psiElement, @No * arguments: ~ */ @Nullable - private static String getServiceClassFromServiceMapping(@NotNull YAMLMapping yamlMapping) { + public static String getServiceClassFromServiceMapping(@NotNull YAMLMapping yamlMapping) { YAMLKeyValue classKeyValue = yamlMapping.getKeyValueByKey("class"); // Symfony 3.3: "class" is optional; use service id for class diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/yaml/YamlHelper.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/yaml/YamlHelper.java index 61864fed9..ba6c171c0 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/yaml/YamlHelper.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/yaml/YamlHelper.java @@ -18,10 +18,15 @@ import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.php.lang.psi.elements.Parameter; +import com.jetbrains.php.lang.psi.elements.PhpClass; import com.jetbrains.php.refactoring.PhpNameUtil; import fr.adrienbrault.idea.symfony2plugin.dic.ParameterResolverConsumer; +import fr.adrienbrault.idea.symfony2plugin.dic.container.util.ServiceContainerUtil; import fr.adrienbrault.idea.symfony2plugin.dic.tags.yaml.StaticAttributeResolver; +import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver; +import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil; import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils; +import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil; import fr.adrienbrault.idea.symfony2plugin.util.yaml.visitor.ParameterVisitor; import fr.adrienbrault.idea.symfony2plugin.util.yaml.visitor.YamlServiceTag; import fr.adrienbrault.idea.symfony2plugin.util.yaml.visitor.YamlTagVisitor; @@ -34,6 +39,7 @@ import org.jetbrains.yaml.YAMLUtil; import org.jetbrains.yaml.psi.*; import org.jetbrains.yaml.psi.impl.YAMLHashImpl; +import org.jetbrains.yaml.psi.impl.YAMLPlainTextImpl; import java.util.*; @@ -1168,4 +1174,33 @@ public boolean accepts(@NotNull PsiElement psiElement, ProcessingContext process return psiElement.getNextSibling() instanceof YAMLMapping; } } + + @NotNull + public static Collection getPhpClassesInYamlFile(@NotNull YAMLFile yamlFile, @NotNull ContainerCollectionResolver.LazyServiceCollector lazyServiceCollector) { + Collection phpClasses = new HashSet<>(); + + for (YAMLKeyValue keyValue : YamlHelper.getQualifiedKeyValuesInFile(yamlFile, "services")) { + YAMLValue value = keyValue.getValue(); + if (value instanceof YAMLMapping) { + // foo.bar: + // classes: ... + String serviceId = ServiceContainerUtil.getServiceClassFromServiceMapping((YAMLMapping) value); + if (StringUtils.isNotBlank(serviceId)) { + PhpClass serviceClass = ServiceUtil.getResolvedClassDefinition(yamlFile.getProject(), serviceId, lazyServiceCollector); + if (serviceClass != null) { + phpClasses.add(serviceClass); + } + } + } else if(value instanceof YAMLPlainTextImpl) { + // Foo\Bar: ~ + String text = keyValue.getKeyText(); + + if (StringUtils.isNotBlank(text) && YamlHelper.isClassServiceId(text)) { + phpClasses.addAll(PhpElementsUtil.getClassesInterface(yamlFile.getProject(), text)); + } + } + } + + return phpClasses; + } } 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 91303165d..921cb5da6 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 @@ -237,6 +237,27 @@ public void testNamedArgumentsNavigationForService() { ); } + public void testNamedArgumentsNavigationForDefaultBinding() { + assertNavigationMatch("services.yml", "" + + "services:\n" + + " _defaults:\n" + + " bind:\n" + + " $i: ~\n"+ + " Foo\\Bar: ~" + + PlatformPatterns.psiElement(Parameter.class) + ); + + assertNavigationMatch("services.yml", "" + + "services:\n" + + " _defaults:\n" + + " bind:\n" + + " $i: ~\n"+ + " foobar:\n" + + " class: Foo\\Bar\n" + + PlatformPatterns.psiElement(Parameter.class) + ); + } + @NotNull private PsiElementPattern.Capture getClassPattern() { return PlatformPatterns.psiElement(PhpClass.class);