Skip to content

Commit dceaf7e

Browse files
committed
add support for named arguments #998
1 parent e6c2e24 commit dceaf7e

File tree

12 files changed

+534
-105
lines changed

12 files changed

+534
-105
lines changed

src/fr/adrienbrault/idea/symfony2plugin/config/xml/XmlHelper.java

+113-13
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import com.intellij.psi.util.PsiTreeUtil;
88
import com.intellij.psi.xml.*;
99
import com.intellij.util.Consumer;
10+
import com.jetbrains.php.lang.psi.elements.Function;
11+
import com.jetbrains.php.lang.psi.elements.Method;
1012
import com.jetbrains.php.lang.psi.elements.Parameter;
1113
import com.jetbrains.php.lang.psi.elements.PhpClass;
1214
import fr.adrienbrault.idea.symfony2plugin.dic.ParameterResolverConsumer;
@@ -630,16 +632,13 @@ public static void visitServiceCallArgument(@NotNull XmlAttributeValue xmlAttrib
630632
XmlTag serviceTag = ((XmlTag) xmlCallTag).getParentTag();
631633
// get service class
632634
if(serviceTag != null && "service".equals(serviceTag.getName())) {
633-
XmlAttribute classAttribute = serviceTag.getAttribute("class");
634-
if(classAttribute != null) {
635-
String className = classAttribute.getValue();
636-
if(className != null) {
637-
consumer.consume(new ParameterVisitor(
638-
className,
639-
methodName,
640-
getArgumentIndex((XmlTag) xmlArgumentTag))
641-
);
642-
}
635+
String className = XmlHelper.getClassFromServiceDefinition(serviceTag);
636+
if(className != null) {
637+
consumer.consume(new ParameterVisitor(
638+
className,
639+
methodName,
640+
getArgumentIndex((XmlTag) xmlArgumentTag)
641+
));
643642
}
644643
}
645644
}
@@ -662,6 +661,107 @@ public static void visitServiceCallArgumentMethodIndex(@NotNull XmlAttributeValu
662661
visitServiceCallArgument(xmlAttribute, new ParameterResolverConsumer(xmlAttribute.getProject(), consumer));
663662
}
664663

664+
665+
/**
666+
* Find argument of given service method scope
667+
*
668+
* <service>
669+
* <argument key="$foobar"/>
670+
* <argument index="0"/>
671+
* <argument/>
672+
* <call method="foobar">
673+
* <argument key="$foobar"/>
674+
* <argument index="0"/>
675+
* <argument/>
676+
* </call>
677+
* </service>
678+
*/
679+
public static int getArgumentIndex(@NotNull XmlTag argumentTag) {
680+
String indexAttr = argumentTag.getAttributeValue("index");
681+
if(indexAttr != null) {
682+
try {
683+
return Integer.valueOf(indexAttr);
684+
} catch (NumberFormatException e) {
685+
return -1;
686+
}
687+
}
688+
689+
String keyAttr = argumentTag.getAttributeValue("key");
690+
if(keyAttr != null) {
691+
PsiElement parentTag = argumentTag.getParent();
692+
if(parentTag instanceof XmlTag) {
693+
String name = ((XmlTag) parentTag).getName();
694+
695+
if("service".equalsIgnoreCase(name)) {
696+
// <service><argument/></service>
697+
String aClass = XmlHelper.getClassFromServiceDefinition((XmlTag) parentTag);
698+
if(aClass != null) {
699+
PhpClass phpClass = ServiceUtil.getResolvedClassDefinition(argumentTag.getProject(), aClass);
700+
if(phpClass != null) {
701+
int parameter = PhpElementsUtil.getConstructorArgumentByName(phpClass, StringUtils.stripStart(keyAttr, "$"));
702+
if(parameter >= 0) {
703+
return parameter;
704+
}
705+
}
706+
}
707+
} else if("call".equalsIgnoreCase(name)) {
708+
// <service><call method="foobar"><argument/></call></service>
709+
PsiElement serviceTag = parentTag.getParent();
710+
if(serviceTag instanceof XmlTag && "service".equalsIgnoreCase(((XmlTag) serviceTag).getName())) {
711+
String methodName = ((XmlTag) parentTag).getAttributeValue("method");
712+
if(methodName != null && StringUtils.isNotBlank(methodName)) {
713+
String aClass = XmlHelper.getClassFromServiceDefinition((XmlTag) serviceTag);
714+
if(aClass != null) {
715+
PhpClass phpClass = ServiceUtil.getResolvedClassDefinition(argumentTag.getProject(), aClass);
716+
if(phpClass != null) {
717+
Method methodByName = phpClass.findMethodByName(methodName);
718+
if(methodByName != null) {
719+
int parameter = PhpElementsUtil.getFunctionArgumentByName(methodByName, StringUtils.stripStart(keyAttr, "$"));
720+
if(parameter >= 0) {
721+
return parameter;
722+
}
723+
}
724+
}
725+
}
726+
}
727+
}
728+
}
729+
}
730+
}
731+
732+
return getArgumentIndexByCount(argumentTag);
733+
}
734+
735+
/**
736+
* Find argument of given service function / method scope
737+
*
738+
* <service>
739+
* <argument key="$foobar"/>
740+
* <argument index="0"/>
741+
* <argument/>
742+
* </service>
743+
*/
744+
public static int getArgumentIndex(@NotNull XmlTag argumentTag, @NotNull Function function) {
745+
String indexAttr = argumentTag.getAttributeValue("index");
746+
if(indexAttr != null) {
747+
try {
748+
return Integer.valueOf(indexAttr);
749+
} catch (NumberFormatException e) {
750+
return -1;
751+
}
752+
}
753+
754+
String keyAttr = argumentTag.getAttributeValue("key");
755+
if(keyAttr != null) {
756+
int parameter = PhpElementsUtil.getFunctionArgumentByName(function, StringUtils.stripStart(keyAttr, "$"));
757+
if(parameter >= 0) {
758+
return parameter;
759+
}
760+
}
761+
762+
return getArgumentIndexByCount(argumentTag);
763+
}
764+
665765
/**
666766
* Returns current index of parent tag
667767
*
@@ -670,14 +770,14 @@ public static void visitServiceCallArgumentMethodIndex(@NotNull XmlAttributeValu
670770
* <arg<caret>ument/>
671771
* </foo>
672772
*/
673-
public static int getArgumentIndex(@NotNull XmlTag xmlTag) {
674-
773+
private static int getArgumentIndexByCount(@NotNull XmlTag xmlTag) {
675774
PsiElement psiElement = xmlTag;
676775
int index = 0;
677776

678777
while (psiElement != null) {
679778
psiElement = psiElement.getPrevSibling();
680-
if(psiElement instanceof XmlTag && "argument".equalsIgnoreCase(((XmlTag) psiElement).getName())) {
779+
// ignore: <argument index="0"/>, <argument key="$foobar"/>
780+
if(psiElement instanceof XmlTag && "argument".equalsIgnoreCase(((XmlTag) psiElement).getName()) && ((XmlTag) psiElement).getAttribute("key") == null && ((XmlTag) psiElement).getAttribute("index") == null) {
681781
index++;
682782
}
683783
}

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

+77-70
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.jetbrains.php.PhpIndex;
1313
import com.jetbrains.php.lang.psi.elements.Field;
1414
import com.jetbrains.php.lang.psi.elements.Method;
15+
import com.jetbrains.php.lang.psi.elements.Parameter;
1516
import com.jetbrains.php.lang.psi.elements.PhpClass;
1617
import fr.adrienbrault.idea.symfony2plugin.config.xml.XmlHelper;
1718
import fr.adrienbrault.idea.symfony2plugin.dic.attribute.value.AttributeValueInterface;
@@ -289,51 +290,76 @@ public static ServiceTypeHint getYamlConstructorTypeHint(@NotNull PsiElement psi
289290
*/
290291
@Nullable
291292
public static ServiceTypeHint getYamlConstructorTypeHint(@NotNull YAMLScalar yamlScalar, @NotNull ContainerCollectionResolver.LazyServiceCollector lazyServiceCollector) {
292-
PsiElement context = yamlScalar.getContext();
293-
if(!(context instanceof YAMLSequenceItem)) {
294-
return null;
295-
}
296-
297-
final YAMLSequenceItem sequenceItem = (YAMLSequenceItem) context;
298-
if (!(sequenceItem.getContext() instanceof YAMLSequence)) {
299-
return null;
300-
}
293+
// arguments: ['$foobar': '@foo']
301294

302-
final YAMLSequence yamlArray = (YAMLSequence) sequenceItem.getContext();
303-
if(!(yamlArray.getContext() instanceof YAMLKeyValue)) {
304-
return null;
305-
}
306-
307-
final YAMLKeyValue yamlKeyValue = (YAMLKeyValue) yamlArray.getContext();
308-
if(!yamlKeyValue.getKeyText().equals("arguments")) {
309-
return null;
310-
}
311-
312-
YAMLMapping parentMapping = yamlKeyValue.getParentMapping();
313-
if(parentMapping == null) {
314-
return null;
315-
}
316-
317-
String serviceId = getServiceClassFromServiceMapping(parentMapping);
318-
if(StringUtils.isBlank(serviceId)) {
319-
return null;
320-
}
321-
322-
PhpClass serviceClass = ServiceUtil.getResolvedClassDefinition(yamlScalar.getProject(), serviceId, lazyServiceCollector);
323-
if(serviceClass == null) {
324-
return null;
295+
PsiElement context = yamlScalar.getContext();
296+
if(context instanceof YAMLKeyValue) {
297+
String key = ((YAMLKeyValue) context).getKeyText();
298+
if(key.startsWith("$")) {
299+
PsiElement yamlMapping = context.getParent();
300+
if(yamlMapping instanceof YAMLMapping) {
301+
PsiElement yamlKeyValue = yamlMapping.getParent();
302+
if(yamlKeyValue instanceof YAMLKeyValue) {
303+
String keyText = ((YAMLKeyValue) yamlKeyValue).getKeyText();
304+
if(keyText.equals("arguments")) {
305+
YAMLMapping parentMapping = ((YAMLKeyValue) yamlKeyValue).getParentMapping();
306+
if(parentMapping != null) {
307+
String serviceId = getServiceClassFromServiceMapping(parentMapping);
308+
if(StringUtils.isNotBlank(serviceId)) {
309+
PhpClass serviceClass = ServiceUtil.getResolvedClassDefinition(yamlScalar.getProject(), serviceId, lazyServiceCollector);
310+
if(serviceClass != null) {
311+
Method constructor = serviceClass.getConstructor();
312+
if(constructor != null) {
313+
Parameter[] parameters = constructor.getParameters();
314+
for (int i = 0; i < parameters.length; i++) {
315+
Parameter parameter = parameters[i];
316+
if (key.equals("$" + parameter.getName())) {
317+
return new ServiceTypeHint(constructor, i, yamlScalar);
318+
}
319+
}
320+
}
321+
}
322+
}
323+
}
324+
}
325+
}
326+
}
327+
}
325328
}
326329

327-
Method constructor = serviceClass.getConstructor();
328-
if(constructor == null) {
329-
return null;
330+
// arguments: ['@foobar']
331+
if(context instanceof YAMLSequenceItem) {
332+
YAMLSequenceItem sequenceItem = (YAMLSequenceItem) context;
333+
PsiElement yamlSequenceItem = sequenceItem.getContext();
334+
if (yamlSequenceItem instanceof YAMLSequence) {
335+
YAMLSequence yamlArray = (YAMLSequence) sequenceItem.getContext();
336+
PsiElement yamlKeyValue = yamlArray.getContext();
337+
if(yamlKeyValue instanceof YAMLKeyValue) {
338+
YAMLKeyValue yamlKeyValueArguments = (YAMLKeyValue) yamlKeyValue;
339+
if(yamlKeyValueArguments.getKeyText().equals("arguments")) {
340+
YAMLMapping parentMapping = yamlKeyValueArguments.getParentMapping();
341+
if(parentMapping != null) {
342+
String serviceId = getServiceClassFromServiceMapping(parentMapping);
343+
if(StringUtils.isNotBlank(serviceId)) {
344+
PhpClass serviceClass = ServiceUtil.getResolvedClassDefinition(yamlScalar.getProject(), serviceId, lazyServiceCollector);
345+
if(serviceClass != null) {
346+
Method constructor = serviceClass.getConstructor();
347+
if(constructor != null) {
348+
return new ServiceTypeHint(
349+
constructor,
350+
PsiElementUtils.getPrevSiblingsOfType(sequenceItem, PlatformPatterns.psiElement(YAMLSequenceItem.class)).size(),
351+
yamlScalar
352+
);
353+
}
354+
}
355+
}
356+
}
357+
}
358+
}
359+
}
330360
}
331361

332-
return new ServiceTypeHint(
333-
constructor,
334-
PsiElementUtils.getPrevSiblingsOfType(sequenceItem, PlatformPatterns.psiElement(YAMLSequenceItem.class)).size(),
335-
yamlScalar
336-
);
362+
return null;
337363
}
338364

339365
/**
@@ -395,13 +421,13 @@ public static ServiceTypeHint getXmlConstructorTypeHint(@NotNull PsiElement psiE
395421
// service/argument[id]
396422
String serviceDefName = XmlHelper.getClassFromServiceDefinition(serviceTag);
397423
if(serviceDefName != null) {
398-
PhpClass phpClass = ServiceUtil.getResolvedClassDefinition(psiElement.getProject(), serviceDefName);
424+
PhpClass phpClass = ServiceUtil.getResolvedClassDefinition(psiElement.getProject(), serviceDefName, lazyServiceCollector);
399425

400426
// check type hint on constructor
401427
if(phpClass != null) {
402428
Method constructor = phpClass.getConstructor();
403429
if(constructor != null) {
404-
return new ServiceTypeHint(constructor, getArgumentIndex(argumentTag), psiElement);
430+
return new ServiceTypeHint(constructor, XmlHelper.getArgumentIndex(argumentTag, constructor), psiElement);
405431
}
406432
}
407433
}
@@ -440,19 +466,15 @@ public static ServiceTypeHint getXmlCallTypeHint(@NotNull PsiElement psiElement,
440466

441467
// get service class
442468
if(serviceTag != null && "service".equals(serviceTag.getName())) {
443-
XmlAttribute classAttribute = serviceTag.getAttribute("class");
444-
if(classAttribute != null) {
445-
446-
String serviceDefName = classAttribute.getValue();
447-
if(serviceDefName != null) {
448-
PhpClass phpClass = ServiceUtil.getResolvedClassDefinition(psiElement.getProject(), serviceDefName);
449-
450-
// finally check method type hint
451-
if(phpClass != null) {
452-
Method method = phpClass.findMethodByName(methodName);
453-
if(method != null) {
454-
return new ServiceTypeHint(method, getArgumentIndex(currentXmlTag), psiElement);
455-
}
469+
String serviceDefName = XmlHelper.getClassFromServiceDefinition(serviceTag);
470+
if(serviceDefName != null) {
471+
PhpClass phpClass = ServiceUtil.getResolvedClassDefinition(psiElement.getProject(), serviceDefName, lazyServiceCollector);
472+
473+
// finally check method type hint
474+
if(phpClass != null) {
475+
Method method = phpClass.findMethodByName(methodName);
476+
if(method != null) {
477+
return new ServiceTypeHint(method, XmlHelper.getArgumentIndex(currentXmlTag, method), psiElement);
456478
}
457479
}
458480
}
@@ -506,21 +528,6 @@ public static int getServiceUsage(@NotNull Project project, @NotNull String id)
506528
return usage;
507529
}
508530

509-
private static int getArgumentIndex(@NotNull XmlTag xmlTag) {
510-
511-
PsiElement psiElement = xmlTag;
512-
int index = 0;
513-
514-
while (psiElement != null) {
515-
psiElement = psiElement.getPrevSibling();
516-
if(psiElement instanceof XmlTag && "argument".equalsIgnoreCase(((XmlTag) psiElement).getName())) {
517-
index++;
518-
}
519-
}
520-
521-
return index;
522-
}
523-
524531
public static boolean isLowerPriority(String name) {
525532
for(String lowerName: LOWER_PRIORITY) {
526533
if(name.contains(lowerName)) {

0 commit comments

Comments
 (0)