Skip to content

Commit e5eecfc

Browse files
committed
add service suggestions for xml call arguments
1 parent 52d437b commit e5eecfc

File tree

8 files changed

+308
-87
lines changed

8 files changed

+308
-87
lines changed

src/fr/adrienbrault/idea/symfony2plugin/dic/ServiceCompletionProvider.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
1111
import fr.adrienbrault.idea.symfony2plugin.action.ui.SymfonyCreateService;
1212
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.ServiceSuggestionCollector;
13+
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.XmlCallServiceSuggestionCollector;
1314
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.XmlConstructServiceSuggestionCollector;
1415
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.YamlConstructServiceSuggestionCollector;
1516
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
@@ -25,6 +26,7 @@ public class ServiceCompletionProvider extends CompletionProvider<CompletionPara
2526
private static ServiceSuggestionCollector[] COLLECTORS = new ServiceSuggestionCollector[] {
2627
new XmlConstructServiceSuggestionCollector(),
2728
new YamlConstructServiceSuggestionCollector(),
29+
new XmlCallServiceSuggestionCollector(),
2830
};
2931

3032
public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet resultSet) {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion;
2+
3+
import com.intellij.psi.PsiElement;
4+
import com.intellij.psi.xml.XmlFile;
5+
import com.intellij.psi.xml.XmlTokenType;
6+
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerService;
7+
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.utils.ServiceSuggestionUtil;
8+
import fr.adrienbrault.idea.symfony2plugin.dic.container.util.ServiceContainerUtil;
9+
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
10+
import org.jetbrains.annotations.NotNull;
11+
12+
import java.util.Collection;
13+
import java.util.Collections;
14+
15+
/**
16+
* @author Daniel Espendiller <daniel@espendiller.net>
17+
*
18+
* <service class="Foo">
19+
* <call method="setFoo">
20+
* <argument type="service" id="<caret>" />
21+
* </call>
22+
* </service>
23+
*/
24+
public class XmlCallServiceSuggestionCollector implements ServiceSuggestionCollector {
25+
26+
@NotNull
27+
public Collection<String> collect(@NotNull PsiElement psiElement, @NotNull Collection<ContainerService> serviceMap) {
28+
if(!(psiElement.getContainingFile() instanceof XmlFile) || psiElement.getNode().getElementType() != XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN) {
29+
return Collections.emptyList();
30+
}
31+
32+
return ServiceSuggestionUtil.createSuggestions(ServiceContainerUtil.getXmlCallTypeHint(
33+
psiElement, new ContainerCollectionResolver.LazyServiceCollector(psiElement.getProject())
34+
), serviceMap);
35+
}
36+
}
Lines changed: 6 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
11
package fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion;
22

33
import com.intellij.psi.PsiElement;
4-
import com.intellij.psi.util.PsiTreeUtil;
5-
import com.intellij.psi.xml.XmlAttributeValue;
64
import com.intellij.psi.xml.XmlFile;
7-
import com.intellij.psi.xml.XmlTag;
85
import com.intellij.psi.xml.XmlTokenType;
9-
import com.intellij.util.Function;
10-
import com.intellij.util.containers.ContainerUtil;
11-
import com.jetbrains.php.lang.psi.elements.Method;
12-
import com.jetbrains.php.lang.psi.elements.PhpClass;
13-
import fr.adrienbrault.idea.symfony2plugin.config.xml.XmlServiceContainerAnnotator;
146
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerService;
15-
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil;
16-
import fr.adrienbrault.idea.symfony2plugin.util.psi.PsiElementAssertUtil;
7+
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.utils.ServiceSuggestionUtil;
8+
import fr.adrienbrault.idea.symfony2plugin.dic.container.util.ServiceContainerUtil;
9+
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
1710
import org.jetbrains.annotations.NotNull;
1811

1912
import java.util.Collection;
@@ -30,58 +23,9 @@ public Collection<String> collect(@NotNull PsiElement psiElement, @NotNull Colle
3023
return Collections.emptyList();
3124
}
3225

33-
XmlAttributeValue xmlAttributeValue = PsiTreeUtil.getParentOfType(psiElement, XmlAttributeValue.class);
34-
if(xmlAttributeValue == null) {
35-
return Collections.emptyList();
36-
}
37-
38-
XmlTag argumentTag = PsiTreeUtil.getParentOfType(psiElement, XmlTag.class);
39-
if(argumentTag == null) {
40-
return Collections.emptyList();
41-
}
42-
43-
XmlTag serviceTag = PsiElementAssertUtil.getParentOfTypeOrNull(argumentTag, XmlTag.class);
44-
if(serviceTag == null) {
45-
return Collections.emptyList();
46-
}
47-
48-
if(!serviceTag.getName().equals("service")) {
49-
return Collections.emptyList();
50-
}
51-
52-
// service/argument[id]
53-
54-
String serviceDefName = serviceTag.getAttributeValue("class");
55-
if(serviceDefName != null) {
56-
PhpClass phpClass = ServiceUtil.getResolvedClassDefinition(psiElement.getProject(), serviceDefName);
57-
58-
// check type hint on constructor
59-
if(phpClass != null) {
60-
Method constructor = phpClass.getConstructor();
61-
if(constructor != null) {
62-
63-
Collection<ContainerService> suggestions = ServiceUtil.getServiceSuggestionsForTypeHint(
64-
constructor,
65-
XmlServiceContainerAnnotator.getArgumentIndex(argumentTag),
66-
serviceMap
67-
);
68-
69-
if(suggestions.size() == 0) {
70-
return Collections.emptyList();
71-
}
72-
73-
return ContainerUtil.map(suggestions, new Function<ContainerService, String>() {
74-
@Override
75-
public String fun(ContainerService containerService) {
76-
return containerService.getName();
77-
}
78-
});
79-
}
80-
}
81-
82-
}
83-
84-
return Collections.emptyList();
26+
return ServiceSuggestionUtil.createSuggestions(ServiceContainerUtil.getXmlConstructorTypeHint(
27+
psiElement, new ContainerCollectionResolver.LazyServiceCollector(psiElement.getProject())
28+
), serviceMap);
8529
}
8630

8731
}
Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
package fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion;
22

33
import com.intellij.psi.PsiElement;
4-
import com.intellij.util.containers.ContainerUtil;
54
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerService;
6-
import fr.adrienbrault.idea.symfony2plugin.dic.container.dict.ServiceTypeHint;
5+
import fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.utils.ServiceSuggestionUtil;
76
import fr.adrienbrault.idea.symfony2plugin.dic.container.util.ServiceContainerUtil;
87
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
9-
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil;
108
import org.jetbrains.annotations.NotNull;
119

1210
import java.util.Collection;
13-
import java.util.Collections;
1411

1512
/**
1613
* @author Daniel Espendiller <daniel@espendiller.net>
@@ -19,24 +16,8 @@ public class YamlConstructServiceSuggestionCollector implements ServiceSuggestio
1916

2017
@NotNull
2118
public Collection<String> collect(@NotNull PsiElement psiElement, @NotNull Collection<ContainerService> serviceMap) {
22-
ServiceTypeHint methodTypeHint = ServiceContainerUtil.getYamlConstructorTypeHint(
19+
return ServiceSuggestionUtil.createSuggestions(ServiceContainerUtil.getYamlConstructorTypeHint(
2320
psiElement, new ContainerCollectionResolver.LazyServiceCollector(psiElement.getProject())
24-
);
25-
26-
if(methodTypeHint == null) {
27-
return Collections.emptyList();
28-
}
29-
30-
Collection<ContainerService> suggestions = ServiceUtil.getServiceSuggestionsForTypeHint(
31-
methodTypeHint.getMethod(),
32-
methodTypeHint.getIndex(),
33-
serviceMap
34-
);
35-
36-
if(suggestions.size() == 0) {
37-
return Collections.emptyList();
38-
}
39-
40-
return ContainerUtil.map(suggestions, ContainerService::getName);
21+
), serviceMap);
4122
}
4223
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package fr.adrienbrault.idea.symfony2plugin.dic.container.suggestion.utils;
2+
3+
import com.intellij.psi.PsiElement;
4+
import com.intellij.util.containers.ContainerUtil;
5+
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerService;
6+
import fr.adrienbrault.idea.symfony2plugin.dic.container.dict.ServiceTypeHint;
7+
import fr.adrienbrault.idea.symfony2plugin.dic.container.util.ServiceContainerUtil;
8+
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
9+
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil;
10+
import org.jetbrains.annotations.NotNull;
11+
import org.jetbrains.annotations.Nullable;
12+
13+
import java.util.Collection;
14+
import java.util.Collections;
15+
16+
/**
17+
* @author Daniel Espendiller <daniel@espendiller.net>
18+
*/
19+
public class ServiceSuggestionUtil {
20+
21+
@NotNull
22+
private static Collection<String> createServiceCollection(@NotNull ServiceTypeHint serviceTypeHint, @NotNull Collection<ContainerService> serviceMap) {
23+
Collection<ContainerService> suggestions = ServiceUtil.getServiceSuggestionsForTypeHint(
24+
serviceTypeHint.getMethod(),
25+
serviceTypeHint.getIndex(),
26+
serviceMap
27+
);
28+
29+
if(suggestions.size() == 0) {
30+
return Collections.emptyList();
31+
}
32+
33+
return ContainerUtil.map(suggestions, ContainerService::getName);
34+
}
35+
36+
@NotNull
37+
public static Collection<String> createSuggestions(@Nullable ServiceTypeHint serviceTypeHint, @NotNull Collection<ContainerService> serviceMap) {
38+
if(serviceTypeHint == null) {
39+
return Collections.emptyList();
40+
}
41+
42+
return ServiceSuggestionUtil.createServiceCollection(serviceTypeHint, serviceMap);
43+
}
44+
}

src/fr/adrienbrault/idea/symfony2plugin/dic/container/util/ServiceContainerUtil.java

Lines changed: 125 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@
55
import com.intellij.psi.PsiElement;
66
import com.intellij.psi.PsiFile;
77
import com.intellij.psi.util.PsiTreeUtil;
8-
import com.intellij.psi.xml.XmlDocument;
9-
import com.intellij.psi.xml.XmlFile;
10-
import com.intellij.psi.xml.XmlTag;
8+
import com.intellij.psi.xml.*;
119
import com.intellij.util.Consumer;
10+
import com.intellij.util.containers.ContainerUtil;
1211
import com.jetbrains.php.lang.psi.elements.Method;
1312
import com.jetbrains.php.lang.psi.elements.PhpClass;
13+
import fr.adrienbrault.idea.symfony2plugin.config.xml.XmlHelper;
14+
import fr.adrienbrault.idea.symfony2plugin.config.xml.XmlServiceContainerAnnotator;
1415
import fr.adrienbrault.idea.symfony2plugin.config.yaml.YamlAnnotator;
16+
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerService;
1517
import fr.adrienbrault.idea.symfony2plugin.dic.attribute.value.AttributeValueInterface;
1618
import fr.adrienbrault.idea.symfony2plugin.dic.attribute.value.XmlTagAttributeValue;
1719
import fr.adrienbrault.idea.symfony2plugin.dic.attribute.value.YamlKeyValueAttributeValue;
@@ -22,6 +24,7 @@
2224
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
2325
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
2426
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil;
27+
import fr.adrienbrault.idea.symfony2plugin.util.psi.PsiElementAssertUtil;
2528
import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper;
2629
import org.apache.commons.lang.StringUtils;
2730
import org.jetbrains.annotations.NotNull;
@@ -30,6 +33,7 @@
3033

3134
import java.util.ArrayList;
3235
import java.util.Collection;
36+
import java.util.Collections;
3337

3438
/**
3539
* @author Daniel Espendiller <daniel@espendiller.net>
@@ -211,4 +215,122 @@ public static ServiceTypeHint getYamlConstructorTypeHint(@NotNull PsiElement psi
211215
psiElement
212216
);
213217
}
218+
219+
/**
220+
* <services>
221+
* <service class="Foo\\Bar\\Car">
222+
* <argument type="service" id="<caret>" />
223+
* </service>
224+
* </services>
225+
*/
226+
@Nullable
227+
public static ServiceTypeHint getXmlConstructorTypeHint(@NotNull PsiElement psiElement, @NotNull ContainerCollectionResolver.LazyServiceCollector lazyServiceCollector) {
228+
if(!(psiElement.getContainingFile() instanceof XmlFile) || psiElement.getNode().getElementType() != XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN) {
229+
return null;
230+
}
231+
232+
XmlAttributeValue xmlAttributeValue = PsiTreeUtil.getParentOfType(psiElement, XmlAttributeValue.class);
233+
if(xmlAttributeValue == null) {
234+
return null;
235+
}
236+
237+
XmlTag argumentTag = PsiTreeUtil.getParentOfType(psiElement, XmlTag.class);
238+
if(argumentTag == null) {
239+
return null;
240+
}
241+
242+
XmlTag serviceTag = PsiElementAssertUtil.getParentOfTypeOrNull(argumentTag, XmlTag.class);
243+
if(serviceTag == null) {
244+
return null;
245+
}
246+
247+
if(!serviceTag.getName().equals("service")) {
248+
return null;
249+
}
250+
251+
// service/argument[id]
252+
String serviceDefName = serviceTag.getAttributeValue("class");
253+
if(serviceDefName != null) {
254+
PhpClass phpClass = ServiceUtil.getResolvedClassDefinition(psiElement.getProject(), serviceDefName);
255+
256+
// check type hint on constructor
257+
if(phpClass != null) {
258+
Method constructor = phpClass.getConstructor();
259+
if(constructor != null) {
260+
return new ServiceTypeHint(constructor, getArgumentIndex(argumentTag), psiElement);
261+
}
262+
}
263+
264+
}
265+
266+
return null;
267+
}
268+
269+
/**
270+
* <services>
271+
* <service class="Foo\\Bar\\Car">
272+
* <call method="foo"></call>
273+
* <argument type="service" id="<caret>" />
274+
* </call>
275+
* </service>
276+
* </services>
277+
*/
278+
@Nullable
279+
public static ServiceTypeHint getXmlCallTypeHint(@NotNull PsiElement psiElement, @NotNull ContainerCollectionResolver.LazyServiceCollector lazyServiceCollector) {
280+
// search for parent service definition
281+
XmlTag currentXmlTag = PsiTreeUtil.getParentOfType(psiElement, XmlTag.class);
282+
XmlTag parentXmlTag = PsiTreeUtil.getParentOfType(currentXmlTag, XmlTag.class);
283+
if(parentXmlTag == null) {
284+
return null;
285+
}
286+
287+
String name = parentXmlTag.getName();
288+
if(!"call".equals(name)) {
289+
return null;
290+
}
291+
292+
// service/call/argument[id]
293+
XmlAttribute methodAttribute = parentXmlTag.getAttribute("method");
294+
if(methodAttribute != null) {
295+
String methodName = methodAttribute.getValue();
296+
XmlTag serviceTag = parentXmlTag.getParentTag();
297+
298+
// get service class
299+
if(serviceTag != null && "service".equals(serviceTag.getName())) {
300+
XmlAttribute classAttribute = serviceTag.getAttribute("class");
301+
if(classAttribute != null) {
302+
303+
String serviceDefName = classAttribute.getValue();
304+
if(serviceDefName != null) {
305+
PhpClass phpClass = ServiceUtil.getResolvedClassDefinition(psiElement.getProject(), serviceDefName);
306+
307+
// finally check method type hint
308+
if(phpClass != null) {
309+
Method method = phpClass.findMethodByName(methodName);
310+
if(method != null) {
311+
return new ServiceTypeHint(method, getArgumentIndex(currentXmlTag), psiElement);
312+
}
313+
}
314+
}
315+
}
316+
}
317+
}
318+
319+
return null;
320+
}
321+
322+
private static int getArgumentIndex(@NotNull XmlTag xmlTag) {
323+
324+
PsiElement psiElement = xmlTag;
325+
int index = 0;
326+
327+
while (psiElement != null) {
328+
psiElement = psiElement.getPrevSibling();
329+
if(psiElement instanceof XmlTag && "argument".equalsIgnoreCase(((XmlTag) psiElement).getName())) {
330+
index++;
331+
}
332+
}
333+
334+
return index;
335+
}
214336
}

0 commit comments

Comments
 (0)