Skip to content

Commit be29e02

Browse files
committed
provide class name completion in xml service ids #967; goto definition does not support services with FQCN xml ids shortcuts #952
1 parent 5450439 commit be29e02

File tree

6 files changed

+127
-27
lines changed

6 files changed

+127
-27
lines changed

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

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package fr.adrienbrault.idea.symfony2plugin.codeInsight;
22

3+
import com.intellij.codeInsight.lookup.LookupElement;
34
import com.intellij.openapi.project.Project;
45
import com.intellij.psi.PsiElement;
56
import org.jetbrains.annotations.NotNull;
67

8+
import java.util.Collection;
9+
import java.util.Collections;
10+
711
/**
812
* @author Daniel Espendiller <daniel@espendiller.net>
913
*/
@@ -25,8 +29,15 @@ protected PsiElement getElement() {
2529
return this.element;
2630
}
2731

32+
@NotNull
33+
@Override
34+
public Collection<LookupElement> getLookupElements() {
35+
return Collections.emptyList();
36+
}
37+
38+
@NotNull
2839
@Override
29-
public void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arguments) {
30-
// empty for compatibility
40+
public Collection<PsiElement> getPsiTargets(PsiElement element) {
41+
return Collections.emptyList();
3142
}
3243
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ public interface GotoCompletionProviderInterfaceEx extends GotoCompletionProvide
1010
* Extended lookup element implementation
1111
* allowing resultSet modification
1212
*/
13-
void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arguments);
13+
default void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arguments) {}
1414
}

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

+19-19
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,14 @@
1515
import com.jetbrains.php.lang.psi.elements.PhpClass;
1616
import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons;
1717
import fr.adrienbrault.idea.symfony2plugin.TwigHelper;
18-
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionContributor;
19-
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProvider;
20-
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrar;
21-
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrarParameter;
18+
import fr.adrienbrault.idea.symfony2plugin.codeInsight.*;
2219
import fr.adrienbrault.idea.symfony2plugin.codeInsight.utils.GotoCompletionUtil;
2320
import fr.adrienbrault.idea.symfony2plugin.completion.DecoratedServiceCompletionProvider;
2421
import fr.adrienbrault.idea.symfony2plugin.config.xml.XmlHelper;
2522
import fr.adrienbrault.idea.symfony2plugin.routing.RouteGotoCompletionProvider;
2623
import fr.adrienbrault.idea.symfony2plugin.templating.TemplateGotoCompletionRegistrar;
2724
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
25+
import fr.adrienbrault.idea.symfony2plugin.util.completion.PhpClassCompletionProvider;
2826
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil;
2927
import fr.adrienbrault.idea.symfony2plugin.util.resource.FileResourceUtil;
3028
import org.apache.commons.lang.StringUtils;
@@ -148,11 +146,8 @@ private ServiceIdCompletionProvider(PsiElement element) {
148146
super(element);
149147
}
150148

151-
@NotNull
152149
@Override
153-
public Collection<LookupElement> getLookupElements() {
154-
Collection<LookupElement> lookupElements = new ArrayList<>();
155-
150+
public void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arguments) {
156151
// find class name of service tag
157152
PsiElement xmlToken = this.getElement();
158153
if(xmlToken instanceof XmlToken) {
@@ -163,23 +158,28 @@ public Collection<LookupElement> getLookupElements() {
163158
PsiElement xmlTag = xmlAttribute.getParent();
164159
if(xmlTag instanceof XmlTag) {
165160
String aClass = ((XmlTag) xmlTag).getAttributeValue("class");
166-
if(aClass != null && StringUtils.isNotBlank(aClass)) {
167-
lookupElements.add(LookupElementBuilder.create(
168-
ServiceUtil.getServiceNameForClass(getProject(), aClass)).withIcon(Symfony2Icons.SERVICE)
161+
if(aClass == null) {
162+
// <service id="Foo\Bar"/>
163+
164+
PhpClassCompletionProvider.addClassCompletion(
165+
arguments.getParameters(),
166+
arguments.getResultSet(),
167+
getElement(),
168+
false
169169
);
170+
} else if(StringUtils.isNotBlank(aClass)) {
171+
// <service id="foo.bar" class="Foo\Bar"/>
172+
173+
LookupElementBuilder lookupElement = LookupElementBuilder
174+
.create(ServiceUtil.getServiceNameForClass(getProject(), aClass))
175+
.withIcon(Symfony2Icons.SERVICE);
176+
177+
arguments.getResultSet().addElement(lookupElement);
170178
}
171179
}
172180
}
173181
}
174182
}
175-
176-
return lookupElements;
177-
}
178-
179-
@NotNull
180-
@Override
181-
public Collection<PsiElement> getPsiTargets(PsiElement element) {
182-
return Collections.emptyList();
183183
}
184184
}
185185

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

+68-5
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
import com.intellij.patterns.XmlPatterns;
44
import com.intellij.psi.*;
5-
import com.intellij.psi.xml.XmlAttributeValue;
6-
import com.intellij.psi.xml.XmlText;
7-
import com.intellij.psi.xml.XmlToken;
5+
import com.intellij.psi.xml.*;
86
import com.intellij.util.ProcessingContext;
97
import com.intellij.util.containers.ContainerUtil;
108
import com.jetbrains.php.lang.psi.elements.Method;
@@ -19,6 +17,7 @@
1917
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
2018
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
2119
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil;
20+
import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper;
2221
import org.apache.commons.lang.StringUtils;
2322
import org.jetbrains.annotations.NotNull;
2423

@@ -74,6 +73,13 @@ public PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement, @No
7473
new ClassPsiReferenceProvider()
7574
);
7675

76+
// Symfoyn 3.3 shortcut
77+
// <service id="Class\Name">
78+
registrar.registerReferenceProvider(
79+
XmlHelper.getAttributePattern("id"),
80+
new ClassAsIdPsiReferenceProvider()
81+
);
82+
7783
// <parameter key="fos_user.user_manager.class">FOS\UserBundle\Doctrine\UserManager</parameter>
7884
registrar.registerReferenceProvider(
7985
XmlHelper.getParameterClassValuePattern(),
@@ -259,8 +265,38 @@ public PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement, @No
259265
return new PsiReference[0];
260266
}
261267

262-
return new PsiReference[]{ new ServiceIdReference(
263-
(XmlAttributeValue) psiElement)
268+
return new PsiReference[] {
269+
new ServiceIdReference((XmlAttributeValue) psiElement)
270+
};
271+
}
272+
}
273+
274+
/**
275+
* Shortcut for service tag without class attribute
276+
*
277+
* <service id="Foobar\Foobar"/>
278+
*/
279+
private static class ClassAsIdPsiReferenceProvider extends PsiReferenceProvider {
280+
@NotNull
281+
@Override
282+
public PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement, @NotNull ProcessingContext processingContext) {
283+
if(!Symfony2ProjectComponent.isEnabled(psiElement) || !(psiElement instanceof XmlAttributeValue)) {
284+
return new PsiReference[0];
285+
}
286+
287+
PsiElement parent = psiElement.getParent();
288+
if(!(parent instanceof XmlAttribute) && YamlHelper.isClassServiceId(parent.getText())) {
289+
return new PsiReference[0];
290+
}
291+
292+
// invalidate on class attribute
293+
PsiElement xmlTag = parent.getParent();
294+
if(!(xmlTag instanceof XmlTag) || ((XmlTag) xmlTag).getAttribute("class") != null) {
295+
return new PsiReference[0];
296+
}
297+
298+
return new PsiReference[] {
299+
new ServiceIdWithoutParameterReference((XmlAttributeValue) psiElement)
264300
};
265301
}
266302
}
@@ -355,6 +391,33 @@ public Object[] getVariants() {
355391

356392
}
357393

394+
/**
395+
* <service id="Foobar\Foo"/>
396+
*/
397+
private static class ServiceIdWithoutParameterReference extends PsiPolyVariantReferenceBase<PsiElement> {
398+
399+
@NotNull
400+
private final XmlAttributeValue psiElement;
401+
402+
private ServiceIdWithoutParameterReference(@NotNull XmlAttributeValue psiElement) {
403+
super(psiElement);
404+
this.psiElement = psiElement;
405+
}
406+
407+
@NotNull
408+
@Override
409+
public ResolveResult[] multiResolve(boolean b) {
410+
String value = this.psiElement.getValue();
411+
return PsiElementResolveResult.createResults(ServiceUtil.getServiceClassTargets(getElement().getProject(), value));
412+
}
413+
414+
@NotNull
415+
@Override
416+
public Object[] getVariants() {
417+
return new Object[0];
418+
}
419+
}
420+
358421
/**
359422
* <factory class="FooBar" method="cre<caret>ate"/>
360423
*/

tests/fr/adrienbrault/idea/symfony2plugin/tests/completion/xml/XmlGotoCompletionRegistrarTest.java

+14
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,20 @@ public void testIdInsideServiceTagShouldCompleteWithClassName() {
5454
);
5555
}
5656

57+
public void testIdInsideServiceTagMustCompleteWithClassNameOnShortcut() {
58+
assertCompletionContains(
59+
XmlFileType.INSTANCE,
60+
"<services><service id=\"<caret>\"/></services>",
61+
"Foobar"
62+
);
63+
64+
assertCompletionContains(
65+
XmlFileType.INSTANCE,
66+
"<services><service id=\"Foo\\<caret>\"/></services>",
67+
"Foo\\Bar"
68+
);
69+
}
70+
5771
public void testThatServiceFactoryMethodAttributeProvidesCompletion() {
5872
assertCompletionContains(XmlFileType.INSTANCE, "" +
5973
"<?xml version=\"1.0\"?>\n" +

tests/fr/adrienbrault/idea/symfony2plugin/tests/config/xml/XmlReferenceContributorTest.java

+12
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,16 @@ public void testEnvironmentParameter() {
121121
PlatformPatterns.psiElement()
122122
);
123123
}
124+
125+
public void testServiceIdAsClassReferences() {
126+
assertReferenceMatchOnParent(XmlFileType.INSTANCE, "" +
127+
"<?xml version=\"1.0\"?>\n" +
128+
"<container>\n" +
129+
" <services>\n" +
130+
" <service id=\"Foo\\<caret>Bar\"/>\n" +
131+
" </services>\n" +
132+
"</container>\n",
133+
PlatformPatterns.psiElement(PhpClass.class).withName("Bar")
134+
);
135+
}
124136
}

0 commit comments

Comments
 (0)