From 619e244fb5b6a069389b180b98a706a88f9418dc Mon Sep 17 00:00:00 2001 From: Daniel Espendiller Date: Wed, 22 Jun 2022 20:35:16 +0200 Subject: [PATCH] named argument completion can now reflect the "bind" or "service" scope --- .../yaml/YamlCompletionContributor.java | 70 +++++++++++++++++-- .../config/yaml/YamlElementPatternHelper.java | 2 +- .../container/util/ServiceContainerUtil.java | 11 +++ .../yaml/YamlCompletionContributorTest.java | 10 +++ .../tests/config/yaml/fixtures/classes.php | 5 ++ 5 files changed, 92 insertions(+), 6 deletions(-) diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/yaml/YamlCompletionContributor.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/yaml/YamlCompletionContributor.java index f6b7b7063..1d31e9ad8 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/yaml/YamlCompletionContributor.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/yaml/YamlCompletionContributor.java @@ -52,8 +52,10 @@ import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.yaml.YAMLTokenTypes; import org.jetbrains.yaml.psi.YAMLCompoundValue; import org.jetbrains.yaml.psi.YAMLKeyValue; +import org.jetbrains.yaml.psi.YAMLMapping; import org.jetbrains.yaml.psi.YAMLScalar; import java.util.*; @@ -265,6 +267,9 @@ public void addCompletions(@NotNull CompletionParameters parameters, // _defaults: // bind: // $: '' + // service: + // arguments: + // $: '' extend( CompletionType.BASIC, YamlElementPatternHelper.getNamedDefaultBindPattern(), @@ -408,16 +413,66 @@ protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull * _defaults: * bind: * $projectDir: '%kernel.project_dir%' + * foobar: + * arguments: + * $projectDir: '%kernel.project_dir%' */ private static class NamedArgumentCompletionProvider extends CompletionProvider { @Override protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) { - HashSet uniqueParameters = new HashSet<>(); - PsiElement position = parameters.getPosition(); boolean hasEmptyNextElement = position.getNextSibling() == null; - ServiceContainerUtil.visitNamedArguments(position.getContainingFile(), pair -> { + YAMLMapping serviceDefintion = null; + + Collection> arguments = new ArrayList<>(); + + // depending on code complete state we already have a key or not + // then decide if we are inside a "service" or "default bind" scope + if (position.getNode().getElementType() == YAMLTokenTypes.SCALAR_KEY) { + // "$name" key exists + YAMLKeyValue parentOfType = PsiTreeUtil.getParentOfType(position, YAMLKeyValue.class); + YAMLKeyValue parentOfType2 = PsiTreeUtil.getParentOfType(parentOfType, YAMLKeyValue.class); + if (parentOfType2 == null) { + return; + } + + String keyText2 = parentOfType2.getKeyText(); + if (!"bind".equals(keyText2)) { + serviceDefintion = parentOfType2.getParentMapping(); + } + } else { + // "$" its just a text element + YAMLKeyValue parentOfType = PsiTreeUtil.getParentOfType(position, YAMLKeyValue.class); + if (parentOfType == null) { + return; + } + + if (!"bind".equals(parentOfType.getKeyText())) { + serviceDefintion = parentOfType.getParentMapping(); + } + } + + ContainerCollectionResolver.LazyServiceCollector lazyServiceCollector = new ContainerCollectionResolver.LazyServiceCollector(position.getProject()); + + // visit full file for arguments or current service scope + if (serviceDefintion == null) { + ServiceContainerUtil.visitNamedArguments(position.getContainingFile(), arguments::add); + } else { + @Nullable PhpClass yamlNamedArgumentPhpClass = ServiceContainerUtil.getServicePhpClassFromServiceMapping(serviceDefintion, lazyServiceCollector); + if (yamlNamedArgumentPhpClass != null) { + Method constructor = yamlNamedArgumentPhpClass.getConstructor(); + if (constructor != null) { + Parameter @NotNull [] constructorParameters = constructor.getParameters(); + for (int i = 0; i < constructorParameters.length; i++) { + arguments.add(Pair.create(constructorParameters[i], i)); + } + } + } + } + + Set uniqueParameters = new HashSet<>(); + for (Pair pair : arguments) { Parameter parameter = pair.getFirst(); String parameterName = parameter.getName(); if (uniqueParameters.contains(parameterName)) { @@ -445,7 +500,7 @@ protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull if (!parameter.getType().getTypes().stream().allMatch(PhpType::isPrimitiveType)) { // $foobar: '@service' - result.addAllElements(getServiceSuggestion(position, pair, parameterName, new ContainerCollectionResolver.LazyServiceCollector(position.getProject()))); + result.addAllElements(getServiceSuggestion(position, pair, parameterName, lazyServiceCollector)); } else { String parameterNormalized = parameterName.toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9]", ""); if (parameterNormalized.length() > 5) { @@ -457,9 +512,14 @@ protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull } } } - }); + } } + /** + * Matched service name on instance + * + * $foobar: '@service' + */ @NotNull private Collection getServiceSuggestion(@NotNull PsiElement position, @NotNull Pair pair, @NotNull String parameterName, @NotNull ContainerCollectionResolver.LazyServiceCollector lazyServiceCollector) { Parameter parameter = pair.getFirst(); 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 80d9ffecf..f99426144 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 @@ -804,7 +804,7 @@ public static ElementPattern getPhpConstPattern() { * arguments: * $ */ - static ElementPattern getNamedDefaultBindPattern() { + public static ElementPattern getNamedDefaultBindPattern() { // "__defaults" key PsiElementPattern.Capture defaultsKey = PlatformPatterns.psiElement(YAMLMapping.class).withParent(PlatformPatterns.psiElement(YAMLKeyValue.class).with(new PatternCondition("KeyText") { @Override 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 296919ee4..750c80f7b 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 @@ -672,6 +672,17 @@ public static String getServiceClassFromServiceMapping(@NotNull YAMLMapping yaml return null; } + @Nullable + public static PhpClass getServicePhpClassFromServiceMapping(@NotNull YAMLMapping yamlMapping, @NotNull ContainerCollectionResolver.LazyServiceCollector lazyServiceCollector) { + String serviceId = getServiceClassFromServiceMapping(yamlMapping); + if (StringUtils.isNotBlank(serviceId)) { + return ServiceUtil.getResolvedClassDefinition(yamlMapping.getProject(), serviceId, lazyServiceCollector); + } + + return null; + } + + /** * * diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/yaml/YamlCompletionContributorTest.java b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/yaml/YamlCompletionContributorTest.java index 6bd3309b9..4a25df140 100644 --- a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/yaml/YamlCompletionContributorTest.java +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/yaml/YamlCompletionContributorTest.java @@ -333,6 +333,16 @@ public void testNamedArgumentCompletionForServiceArguments() { "$myDateTime: '@foo'" ); + assertCompletionNotContains(YAMLFileType.YML, "" + + "services:\n" + + " Foo\\Bus: ~\n" + + "\n" + + " Foo\\Car:\n" + + " arguments:\n" + + " $\n", + "$myBus" + ); + assertCompletionContains(YAMLFileType.YML, "" + "services:\n" + " Foo\\Car:\n" + diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/yaml/fixtures/classes.php b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/yaml/fixtures/classes.php index d158dcdc9..cf88a734e 100644 --- a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/yaml/fixtures/classes.php +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/yaml/fixtures/classes.php @@ -26,4 +26,9 @@ function __construct(\string $projectDir, \string $foobarEnv, \MyDateTime $myDat { } } + + class Bus + { + function __construct($myBus) { } + } } \ No newline at end of file