Skip to content

Added service name reference contributor for YAML DIC files #1919

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package fr.adrienbrault.idea.symfony2plugin.config.yaml;

import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import fr.adrienbrault.idea.symfony2plugin.dic.AbstractServiceReference;
import org.jetbrains.annotations.NotNull;

public class ServiceYamlReference extends AbstractServiceReference {

public ServiceYamlReference(@NotNull PsiElement psiElement, @NotNull String serviceId) {
super(psiElement);

this.serviceId = serviceId;
}

public ServiceYamlReference(@NotNull PsiElement psiElement, @NotNull TextRange range, @NotNull String serviceId) {
super(psiElement, range);

this.serviceId = serviceId;
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package fr.adrienbrault.idea.symfony2plugin.config.yaml;

import com.intellij.openapi.util.TextRange;
import com.intellij.patterns.PatternCondition;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.patterns.StandardPatterns;
import com.intellij.psi.*;
import com.intellij.util.ProcessingContext;
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.yaml.psi.YAMLScalar;
import org.jetbrains.yaml.psi.*;

public class YamlReferenceContributor extends PsiReferenceContributor {
private static final String TAG_PHP_CONST = "!php/const";
Expand All @@ -26,7 +28,7 @@ public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar)
return PsiReference.EMPTY_ARRAY;
}

var scalar = (YAMLScalar)element;
var scalar = (YAMLScalar) element;
if (scalar.getTextValue().isEmpty()) {
return PsiReference.EMPTY_ARRAY;
}
Expand All @@ -37,5 +39,245 @@ public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar)
}
}
);

// services:
// app.service.foo:
// arguments:
// - '@app.service.bar<caret>'
registrar.registerReferenceProvider(
PlatformPatterns
.psiElement(YAMLScalar.class)
.withParent(
PlatformPatterns
.psiElement(YAMLSequenceItem.class)
.withParent(
PlatformPatterns
.psiElement(YAMLSequence.class)
.withParent(
PlatformPatterns.psiElement(YAMLKeyValue.class).withName("arguments")
)
)
),
new YAMLScalarServiceReferenceProvider()
);

// services:
// app.service.foo:
// arguments:
// $bar: '@app.service.bar'

// services:
// app.service.foo:
// properties:
// bar: '@app.service.bar'

registrar.registerReferenceProvider(
PlatformPatterns
.psiElement(YAMLScalar.class)
.withParent(
PlatformPatterns
.psiElement(YAMLKeyValue.class)
.withParent(
PlatformPatterns
.psiElement(YAMLMapping.class)
.withParent(
PlatformPatterns
.psiElement(YAMLKeyValue.class)
.withName("arguments", "properties")
)
)
),
new YAMLScalarServiceReferenceProvider()
);

// services:
// app.service.foo:
// alias: app.service.bar

// services:
// app.service.foo_decorator:
// decorates: app.service.foo

// services:
// app.service.foo:
// parent: app.service.foo_parent

registrar.registerReferenceProvider(
PlatformPatterns
.psiElement(YAMLScalar.class)
.withParent(
PlatformPatterns
.psiElement(YAMLKeyValue.class)
.withName("alias", "decorates", "parent")
),
new YAMLScalarServiceReferenceProvider(false)
);

// services:
// app.service.foo:
// configurator: @app.service.foo_configurator

// services:
// app.service.foo:
// factory: @app.service.foo_factory

registrar.registerReferenceProvider(
PlatformPatterns
.psiElement(YAMLScalar.class)
.withParent(
PlatformPatterns
.psiElement(YAMLKeyValue.class)
.withName("configurator", "factory")
),
new YAMLScalarServiceReferenceProvider()
);

// services:
// app.service.foo:
// factory: ['@app.service.foo_factory', 'create']

// services:
// app.service.foo:
// configurator: ['@app.service.foo_configurator', 'configure']

registrar.registerReferenceProvider(
PlatformPatterns
.psiElement(YAMLScalar.class)
.withParent(
PlatformPatterns
.psiElement(YAMLSequenceItem.class)
.with(new PatternCondition<>("is first sequence item") {
@Override
public boolean accepts(@NotNull YAMLSequenceItem element, ProcessingContext context) {
return element.getItemIndex() == 0;
}
})
.withParent(
PlatformPatterns
.psiElement(YAMLSequence.class)
.withParent(
PlatformPatterns.psiElement(YAMLKeyValue.class).withName("factory", "configurator")
)
)
),
new YAMLScalarServiceReferenceProvider()
);

// services:
// app.service.foo: '@app.service.bar'
registrar.registerReferenceProvider(
PlatformPatterns
.psiElement(YAMLScalar.class)
.withParent(
PlatformPatterns
.psiElement(YAMLKeyValue.class)
.withParent(
PlatformPatterns
.psiElement(YAMLMapping.class)
.withParent(
PlatformPatterns
.psiElement(YAMLKeyValue.class)
.withName("services")
)
)
),
new YAMLScalarServiceReferenceProvider()
);

// services:
// app.service.foo:
// calls:
// - setBar: [ '@app.service.bar' ]
registrar.registerReferenceProvider(
PlatformPatterns
.psiElement(YAMLScalar.class)
.withParent(
PlatformPatterns
.psiElement(YAMLSequenceItem.class)
.withParent(
PlatformPatterns
.psiElement(YAMLSequence.class)
.withParent(
PlatformPatterns
.psiElement(YAMLKeyValue.class)
.withParent(
PlatformPatterns
.psiElement(YAMLMapping.class)
.withParent(
PlatformPatterns
.psiElement(YAMLSequenceItem.class)
.withParent(
PlatformPatterns
.psiElement(YAMLSequence.class)
.withParent(
PlatformPatterns
.psiElement(YAMLKeyValue.class)
.withName("calls")
)
)
)
)

)
)
),
Comment on lines +192 to +223
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any suggestion how this could be simplified will be more the welcome 🤔

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as long as its tested its fine, thats how its nested :)

i also have no idea to simplify this.

new YAMLScalarServiceReferenceProvider()
);
}

private static class YAMLScalarServiceReferenceProvider extends PsiReferenceProvider {

private static final String PREFIX = "@";
private static final String ESCAPED_PREFIX = "@@";

/**
* Flag indicating whenever YAMLScalar value start with `@` prefix
*/
private boolean isPrefixed = true;

public YAMLScalarServiceReferenceProvider() {
}

public YAMLScalarServiceReferenceProvider(boolean isPrefixed) {
this.isPrefixed = isPrefixed;
}

@Override
public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
if (!Symfony2ProjectComponent.isEnabled(element)) {
return PsiReference.EMPTY_ARRAY;
}

if (element instanceof YAMLScalar) {
var serviceName = ((YAMLScalar) element).getTextValue();
if (serviceName.isEmpty()) {
return PsiReference.EMPTY_ARRAY;
}

if (!isPrefixed) {
return new PsiReference[]{
new ServiceYamlReference(element, serviceName)
};
}

if (isValidServiceNameWithPrefix(serviceName)) {
var range = TextRange.from(serviceName.indexOf(PREFIX) + 1, serviceName.length() - 1);
if (element instanceof YAMLQuotedText) {
// Skip quotes
range = range.shiftRight(1);
}

return new PsiReference[]{
new ServiceYamlReference(element, range, serviceName.substring(1))
};
}
}

return PsiReference.EMPTY_ARRAY;
}

private boolean isValidServiceNameWithPrefix(@NotNull String serviceName) {
return serviceName.length() > 1 && serviceName.startsWith(PREFIX) && !serviceName.startsWith(ESCAPED_PREFIX);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package fr.adrienbrault.idea.symfony2plugin.dic;

import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementResolveResult;
import com.intellij.psi.PsiPolyVariantReferenceBase;
import com.intellij.psi.ResolveResult;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.php.PhpIndex;
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
import fr.adrienbrault.idea.symfony2plugin.stubs.ServiceIndexUtil;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

Expand All @@ -26,19 +29,34 @@ public AbstractServiceReference(PsiElement psiElement) {
super(psiElement);
}

@NotNull
public AbstractServiceReference(PsiElement psiElement, TextRange range) {
super(psiElement, range);
}

@Override
public ResolveResult[] multiResolve(boolean incompleteCode) {
public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) {
var definitions = ServiceIndexUtil.findServiceDefinitions(
getElement().getProject(),
serviceId
);

// Return the PsiElement for the service definition corresponding to the serviceId
var results = new ArrayList<ResolveResult>();
for (var definition : definitions) {
results.add(new PsiElementResolveResult(definition));
}

ContainerCollectionResolver.ServiceCollector collector = ContainerCollectionResolver
.ServiceCollector.create(getElement().getProject());

// Return the PsiElement for the class corresponding to the serviceId
String serviceClass = collector.resolve(serviceId);
if (serviceClass == null) {
return new ResolveResult[0];
if (serviceClass != null) {
var classes = PsiElementResolveResult.createResults(PhpIndex.getInstance(getElement().getProject()).getAnyByFQN(serviceClass));
results.addAll(Arrays.asList(classes));
}

return PsiElementResolveResult.createResults(PhpIndex.getInstance(getElement().getProject()).getAnyByFQN(serviceClass));
return results.toArray(ResolveResult.EMPTY_ARRAY);
}

@NotNull
Expand Down
Loading