Skip to content

Commit 769b48f

Browse files
committed
Service ids should be autocompleted for decorates #834
1 parent 30bb6ad commit 769b48f

16 files changed

+377
-14
lines changed

src/fr/adrienbrault/idea/symfony2plugin/codeInsight/GotoCompletionProvider.java

+10-3
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,28 @@
22

33
import com.intellij.openapi.project.Project;
44
import com.intellij.psi.PsiElement;
5+
import org.jetbrains.annotations.NotNull;
56

6-
public abstract class GotoCompletionProvider implements GotoCompletionProviderInterface {
7-
7+
public abstract class GotoCompletionProvider implements GotoCompletionProviderInterfaceEx {
8+
@NotNull
89
private final PsiElement element;
910

10-
public GotoCompletionProvider(PsiElement element) {
11+
public GotoCompletionProvider(@NotNull PsiElement element) {
1112
this.element = element;
1213
}
1314

15+
@NotNull
1416
protected Project getProject() {
1517
return this.element.getProject();
1618
}
1719

20+
@NotNull
1821
protected PsiElement getElement() {
1922
return this.element;
2023
}
2124

25+
@Override
26+
public void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arguments) {
27+
// empty for compatibility
28+
}
2229
}

src/fr/adrienbrault/idea/symfony2plugin/codeInsight/GotoCompletionProviderInterface.java

-2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@
77
import java.util.Collection;
88

99
public interface GotoCompletionProviderInterface {
10-
1110
@NotNull
1211
Collection<LookupElement> getLookupElements();
1312

1413
@NotNull
1514
Collection<PsiElement> getPsiTargets(PsiElement element);
16-
1715
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package fr.adrienbrault.idea.symfony2plugin.codeInsight;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
5+
/**
6+
* @author Daniel Espendiller <daniel@espendiller.net>
7+
*/
8+
public interface GotoCompletionProviderInterfaceEx extends GotoCompletionProviderInterface {
9+
/**
10+
* Extended lookup element implementation
11+
* allowing resultSet modification
12+
*/
13+
void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arguments);
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package fr.adrienbrault.idea.symfony2plugin.codeInsight;
2+
3+
import com.intellij.codeInsight.completion.CompletionParameters;
4+
import com.intellij.codeInsight.completion.CompletionResultSet;
5+
import com.intellij.codeInsight.lookup.LookupElement;
6+
import com.intellij.util.ProcessingContext;
7+
import org.jetbrains.annotations.NotNull;
8+
9+
/**
10+
* @author Daniel Espendiller <daniel@espendiller.net>
11+
*/
12+
public class GotoCompletionProviderLookupArguments {
13+
@NotNull
14+
private final CompletionParameters parameters;
15+
16+
@NotNull
17+
private final ProcessingContext context;
18+
19+
@NotNull
20+
private final CompletionResultSet resultSet;
21+
22+
public GotoCompletionProviderLookupArguments(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet resultSet) {
23+
this.parameters = parameters;
24+
this.context = context;
25+
this.resultSet = resultSet;
26+
}
27+
28+
@NotNull
29+
public CompletionParameters getParameters() {
30+
return parameters;
31+
}
32+
33+
@NotNull
34+
public ProcessingContext getContext() {
35+
return context;
36+
}
37+
38+
@NotNull
39+
public CompletionResultSet getResultSet() {
40+
return resultSet;
41+
}
42+
43+
public void addAllElements(@NotNull Iterable<? extends LookupElement> elements) {
44+
resultSet.addAllElements(elements);
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
package fr.adrienbrault.idea.symfony2plugin.codeInsight;
22

33
public interface GotoCompletionRegistrar {
4-
public void register(GotoCompletionRegistrarParameter registrar);
4+
void register(GotoCompletionRegistrarParameter registrar);
55
}

src/fr/adrienbrault/idea/symfony2plugin/codeInsight/completion/CompletionContributor.java

+12-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
1111
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionContributor;
1212
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProviderInterface;
13+
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProviderInterfaceEx;
14+
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProviderLookupArguments;
1315
import fr.adrienbrault.idea.symfony2plugin.codeInsight.utils.GotoCompletionUtil;
1416
import org.jetbrains.annotations.NotNull;
1517

@@ -27,6 +29,7 @@ protected void addCompletions(@NotNull CompletionParameters completionParameters
2729
return;
2830
}
2931

32+
GotoCompletionProviderLookupArguments arguments = null;
3033
Collection<GotoCompletionContributor> contributors = GotoCompletionUtil.getContributors(psiElement);
3134
for(GotoCompletionContributor contributor: contributors) {
3235
GotoCompletionProviderInterface formReferenceCompletionContributor = contributor.getProvider(psiElement);
@@ -35,10 +38,17 @@ protected void addCompletions(@NotNull CompletionParameters completionParameters
3538
formReferenceCompletionContributor.getLookupElements()
3639
);
3740
}
38-
}
3941

42+
// extension to provide full argument pipes
43+
if(formReferenceCompletionContributor instanceof GotoCompletionProviderInterfaceEx) {
44+
if(arguments == null) {
45+
arguments = new GotoCompletionProviderLookupArguments(completionParameters, processingContext, completionResultSet);
46+
}
47+
48+
((GotoCompletionProviderInterfaceEx) formReferenceCompletionContributor).getLookupElements(arguments);
49+
}
50+
}
4051
}
4152
});
4253
}
43-
4454
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package fr.adrienbrault.idea.symfony2plugin.completion;
2+
3+
import com.intellij.codeInsight.lookup.LookupElement;
4+
import com.intellij.psi.PsiElement;
5+
import com.intellij.psi.util.PsiTreeUtil;
6+
import com.intellij.psi.xml.XmlTag;
7+
import com.jetbrains.php.lang.psi.elements.PhpClass;
8+
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProvider;
9+
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProviderLookupArguments;
10+
import fr.adrienbrault.idea.symfony2plugin.codeInsight.utils.GotoCompletionUtil;
11+
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerService;
12+
import fr.adrienbrault.idea.symfony2plugin.dic.ServiceCompletionProvider;
13+
import fr.adrienbrault.idea.symfony2plugin.dic.ServiceStringLookupElement;
14+
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
15+
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil;
16+
import org.jetbrains.annotations.NotNull;
17+
import org.jetbrains.annotations.Nullable;
18+
19+
import java.util.*;
20+
import java.util.function.Function;
21+
import java.util.stream.Collectors;
22+
23+
/**
24+
* @author Daniel Espendiller <daniel@espendiller.net>
25+
*/
26+
abstract public class DecoratedServiceCompletionProvider extends GotoCompletionProvider {
27+
public DecoratedServiceCompletionProvider(PsiElement psiElement) {
28+
super(psiElement);
29+
}
30+
31+
@Override
32+
public void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arguments) {
33+
ContainerCollectionResolver.LazyServiceCollector lazyServiceCollector = new ContainerCollectionResolver.LazyServiceCollector(getProject());
34+
Map<String, ContainerService> services = lazyServiceCollector.getCollector().getServices();
35+
36+
Collection<String> servicesMatches = collectDecorateServiceSuggestions(lazyServiceCollector);
37+
38+
Collection<LookupElement> collect = services.values().stream()
39+
.map((Function<ContainerService, LookupElement>)
40+
service -> new ServiceStringLookupElement(service, servicesMatches.contains(service.getName())))
41+
.collect(Collectors.toList());
42+
43+
ServiceCompletionProvider.addPrioritizedServiceLookupElements(
44+
arguments.getParameters(),
45+
arguments.getResultSet(),
46+
new ServiceCompletionProvider.PrioritizedLookupResult(collect, servicesMatches)
47+
);
48+
}
49+
50+
@NotNull
51+
@Override
52+
public Collection<LookupElement> getLookupElements() {
53+
return Collections.emptyList();
54+
}
55+
56+
@NotNull
57+
@Override
58+
public Collection<PsiElement> getPsiTargets(PsiElement element) {
59+
String decoratesId = GotoCompletionUtil.getTextValueForElement(element);
60+
if(decoratesId == null) {
61+
return Collections.emptyList();
62+
}
63+
64+
return Collections.singletonList(ServiceUtil.getResolvedClassDefinition(getProject(), decoratesId));
65+
}
66+
67+
@Nullable
68+
abstract public String findClassForElement(@NotNull PsiElement psiElement);
69+
70+
@Nullable
71+
abstract public String findIdForElement(@NotNull PsiElement psiElement);
72+
73+
@NotNull
74+
private Collection<String> collectDecorateServiceSuggestions(@NotNull ContainerCollectionResolver.LazyServiceCollector lazyServiceCollector) {
75+
String classForElement = findClassForElement(getElement());
76+
if(classForElement == null) {
77+
return Collections.emptyList();
78+
}
79+
80+
return createSuggestions(lazyServiceCollector, classForElement, findIdForElement(getElement()));
81+
}
82+
83+
@NotNull
84+
private Collection<String> createSuggestions(@NotNull ContainerCollectionResolver.LazyServiceCollector lazyServiceCollector, @NotNull String aClass, @Nullable String myId) {
85+
Set<String> servicesMatches = new HashSet<>();
86+
87+
PhpClass phpClass = ServiceUtil.getResolvedClassDefinition(getProject(), aClass, lazyServiceCollector);
88+
if(phpClass != null) {
89+
for (PhpClass serviceClass : ServiceUtil.getSuperClasses(phpClass)) {
90+
servicesMatches.addAll(ServiceUtil.getServiceSuggestionForPhpClass(serviceClass, lazyServiceCollector.getCollector().getServices()).stream()
91+
.map(ContainerService::getName).collect(Collectors.toSet())
92+
);
93+
}
94+
}
95+
96+
servicesMatches.remove(myId);
97+
98+
return servicesMatches;
99+
}
100+
}

src/fr/adrienbrault/idea/symfony2plugin/completion/xml/XmlGotoCompletionRegistrar.java

+42
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.intellij.patterns.XmlPatterns;
66
import com.intellij.psi.PsiElement;
77
import com.intellij.psi.PsiFile;
8+
import com.intellij.psi.util.PsiTreeUtil;
89
import com.intellij.psi.xml.XmlAttribute;
910
import com.intellij.psi.xml.XmlAttributeValue;
1011
import com.intellij.psi.xml.XmlTag;
@@ -17,6 +18,7 @@
1718
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrar;
1819
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrarParameter;
1920
import fr.adrienbrault.idea.symfony2plugin.codeInsight.utils.GotoCompletionUtil;
21+
import fr.adrienbrault.idea.symfony2plugin.completion.DecoratedServiceCompletionProvider;
2022
import fr.adrienbrault.idea.symfony2plugin.config.xml.XmlHelper;
2123
import fr.adrienbrault.idea.symfony2plugin.routing.RouteGotoCompletionProvider;
2224
import fr.adrienbrault.idea.symfony2plugin.templating.TemplateGotoCompletionRegistrar;
@@ -25,6 +27,7 @@
2527
import fr.adrienbrault.idea.symfony2plugin.util.resource.FileResourceUtil;
2628
import org.apache.commons.lang.StringUtils;
2729
import org.jetbrains.annotations.NotNull;
30+
import org.jetbrains.annotations.Nullable;
2831

2932
import java.util.ArrayList;
3033
import java.util.Collection;
@@ -67,6 +70,14 @@ public void register(GotoCompletionRegistrarParameter registrar) {
6770
XmlHelper.getRouteDefaultWithKeyAttributePattern("template"),
6871
TemplateGotoCompletionRegistrar::new
6972
);
73+
74+
// <service decorates="<caret>"/>
75+
registrar.register(
76+
XmlPatterns.psiElement().withParent(XmlHelper.getTagAttributePattern("service", "decorates")
77+
.inside(XmlHelper.getInsideTagPattern("services"))
78+
.inFile(XmlHelper.getXmlFilePattern())),
79+
MyDecoratedServiceCompletionProvider::new
80+
);
7081
}
7182

7283
private static class ImportResourceGotoCompletionProvider extends GotoCompletionProvider {
@@ -179,4 +190,35 @@ public Collection<PsiElement> getPsiTargets(PsiElement element) {
179190
return Collections.emptyList();
180191
}
181192
}
193+
194+
private static class MyDecoratedServiceCompletionProvider extends DecoratedServiceCompletionProvider {
195+
MyDecoratedServiceCompletionProvider(PsiElement psiElement) {
196+
super(psiElement);
197+
}
198+
199+
@Nullable
200+
@Override
201+
public String findClassForElement(@NotNull PsiElement psiElement) {
202+
XmlTag parentOfType = PsiTreeUtil.getParentOfType(psiElement, XmlTag.class);
203+
if (parentOfType != null) {
204+
String aClass = parentOfType.getAttributeValue("class");
205+
if (StringUtils.isNotBlank(aClass)) {
206+
return aClass;
207+
}
208+
}
209+
210+
return null;
211+
}
212+
213+
@Nullable
214+
@Override
215+
public String findIdForElement(@NotNull PsiElement psiElement) {
216+
XmlTag parentOfType = PsiTreeUtil.getParentOfType(psiElement, XmlTag.class);
217+
if(parentOfType == null) {
218+
return null;
219+
}
220+
221+
return parentOfType.getAttributeValue("id");
222+
}
223+
}
182224
}

src/fr/adrienbrault/idea/symfony2plugin/completion/yaml/YamlGotoCompletionRegistrar.java

+42
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
package fr.adrienbrault.idea.symfony2plugin.completion.yaml;
22

3+
import com.intellij.psi.PsiElement;
4+
import com.intellij.psi.util.PsiTreeUtil;
35
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrar;
46
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrarParameter;
7+
import fr.adrienbrault.idea.symfony2plugin.completion.DecoratedServiceCompletionProvider;
58
import fr.adrienbrault.idea.symfony2plugin.config.yaml.YamlElementPatternHelper;
69
import fr.adrienbrault.idea.symfony2plugin.routing.RouteGotoCompletionProvider;
710
import fr.adrienbrault.idea.symfony2plugin.templating.TemplateGotoCompletionRegistrar;
11+
import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper;
12+
import org.jetbrains.annotations.NotNull;
13+
import org.jetbrains.annotations.Nullable;
14+
import org.jetbrains.yaml.psi.YAMLMapping;
815

916
public class YamlGotoCompletionRegistrar implements GotoCompletionRegistrar {
1017

@@ -23,5 +30,40 @@ public void register(GotoCompletionRegistrarParameter registrar) {
2330
YamlElementPatternHelper.getSingleLineScalarKey("template"),
2431
TemplateGotoCompletionRegistrar::new
2532
);
33+
34+
// foo.service:
35+
// decorates: <caret>
36+
registrar.register(
37+
YamlElementPatternHelper.getSingleLineScalarKey("decorates"),
38+
MyDecoratedServiceCompletionProvider::new
39+
);
40+
}
41+
42+
private static class MyDecoratedServiceCompletionProvider extends DecoratedServiceCompletionProvider {
43+
MyDecoratedServiceCompletionProvider(PsiElement psiElement) {
44+
super(psiElement);
45+
}
46+
47+
@Nullable
48+
@Override
49+
public String findClassForElement(@NotNull PsiElement psiElement) {
50+
YAMLMapping parentOfType = PsiTreeUtil.getParentOfType(psiElement, YAMLMapping.class);
51+
if(parentOfType == null) {
52+
return null;
53+
}
54+
55+
return YamlHelper.getYamlKeyValueAsString(parentOfType, "class");
56+
}
57+
58+
@Nullable
59+
@Override
60+
public String findIdForElement(@NotNull PsiElement psiElement) {
61+
YAMLMapping parentOfType = PsiTreeUtil.getParentOfType(psiElement, YAMLMapping.class);
62+
if(parentOfType == null) {
63+
return null;
64+
}
65+
66+
return YamlHelper.getYamlKeyValueAsString(parentOfType, "id");
67+
}
2668
}
2769
}

0 commit comments

Comments
 (0)