Skip to content

Commit eb13d45

Browse files
authored
Merge pull request #1919 from adamwojs/service_ref_provider
Added service name reference contributor for YAML DIC files
2 parents 9c6bc14 + d7281c2 commit eb13d45

File tree

6 files changed

+502
-19
lines changed

6 files changed

+502
-19
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package fr.adrienbrault.idea.symfony2plugin.config.yaml;
2+
3+
import com.intellij.openapi.util.TextRange;
4+
import com.intellij.psi.PsiElement;
5+
import fr.adrienbrault.idea.symfony2plugin.dic.AbstractServiceReference;
6+
import org.jetbrains.annotations.NotNull;
7+
8+
public class ServiceYamlReference extends AbstractServiceReference {
9+
10+
public ServiceYamlReference(@NotNull PsiElement psiElement, @NotNull String serviceId) {
11+
super(psiElement);
12+
13+
this.serviceId = serviceId;
14+
}
15+
16+
public ServiceYamlReference(@NotNull PsiElement psiElement, @NotNull TextRange range, @NotNull String serviceId) {
17+
super(psiElement, range);
18+
19+
this.serviceId = serviceId;
20+
}
21+
}

src/main/java/fr/adrienbrault/idea/symfony2plugin/config/yaml/YamlReferenceContributor.java

+244-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package fr.adrienbrault.idea.symfony2plugin.config.yaml;
22

3+
import com.intellij.openapi.util.TextRange;
4+
import com.intellij.patterns.PatternCondition;
35
import com.intellij.patterns.PlatformPatterns;
46
import com.intellij.patterns.StandardPatterns;
57
import com.intellij.psi.*;
68
import com.intellij.util.ProcessingContext;
79
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
810
import org.jetbrains.annotations.NotNull;
9-
import org.jetbrains.yaml.psi.YAMLScalar;
11+
import org.jetbrains.yaml.psi.*;
1012

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

29-
var scalar = (YAMLScalar)element;
31+
var scalar = (YAMLScalar) element;
3032
if (scalar.getTextValue().isEmpty()) {
3133
return PsiReference.EMPTY_ARRAY;
3234
}
@@ -37,5 +39,245 @@ public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar)
3739
}
3840
}
3941
);
42+
43+
// services:
44+
// app.service.foo:
45+
// arguments:
46+
// - '@app.service.bar<caret>'
47+
registrar.registerReferenceProvider(
48+
PlatformPatterns
49+
.psiElement(YAMLScalar.class)
50+
.withParent(
51+
PlatformPatterns
52+
.psiElement(YAMLSequenceItem.class)
53+
.withParent(
54+
PlatformPatterns
55+
.psiElement(YAMLSequence.class)
56+
.withParent(
57+
PlatformPatterns.psiElement(YAMLKeyValue.class).withName("arguments")
58+
)
59+
)
60+
),
61+
new YAMLScalarServiceReferenceProvider()
62+
);
63+
64+
// services:
65+
// app.service.foo:
66+
// arguments:
67+
// $bar: '@app.service.bar'
68+
69+
// services:
70+
// app.service.foo:
71+
// properties:
72+
// bar: '@app.service.bar'
73+
74+
registrar.registerReferenceProvider(
75+
PlatformPatterns
76+
.psiElement(YAMLScalar.class)
77+
.withParent(
78+
PlatformPatterns
79+
.psiElement(YAMLKeyValue.class)
80+
.withParent(
81+
PlatformPatterns
82+
.psiElement(YAMLMapping.class)
83+
.withParent(
84+
PlatformPatterns
85+
.psiElement(YAMLKeyValue.class)
86+
.withName("arguments", "properties")
87+
)
88+
)
89+
),
90+
new YAMLScalarServiceReferenceProvider()
91+
);
92+
93+
// services:
94+
// app.service.foo:
95+
// alias: app.service.bar
96+
97+
// services:
98+
// app.service.foo_decorator:
99+
// decorates: app.service.foo
100+
101+
// services:
102+
// app.service.foo:
103+
// parent: app.service.foo_parent
104+
105+
registrar.registerReferenceProvider(
106+
PlatformPatterns
107+
.psiElement(YAMLScalar.class)
108+
.withParent(
109+
PlatformPatterns
110+
.psiElement(YAMLKeyValue.class)
111+
.withName("alias", "decorates", "parent")
112+
),
113+
new YAMLScalarServiceReferenceProvider(false)
114+
);
115+
116+
// services:
117+
// app.service.foo:
118+
// configurator: @app.service.foo_configurator
119+
120+
// services:
121+
// app.service.foo:
122+
// factory: @app.service.foo_factory
123+
124+
registrar.registerReferenceProvider(
125+
PlatformPatterns
126+
.psiElement(YAMLScalar.class)
127+
.withParent(
128+
PlatformPatterns
129+
.psiElement(YAMLKeyValue.class)
130+
.withName("configurator", "factory")
131+
),
132+
new YAMLScalarServiceReferenceProvider()
133+
);
134+
135+
// services:
136+
// app.service.foo:
137+
// factory: ['@app.service.foo_factory', 'create']
138+
139+
// services:
140+
// app.service.foo:
141+
// configurator: ['@app.service.foo_configurator', 'configure']
142+
143+
registrar.registerReferenceProvider(
144+
PlatformPatterns
145+
.psiElement(YAMLScalar.class)
146+
.withParent(
147+
PlatformPatterns
148+
.psiElement(YAMLSequenceItem.class)
149+
.with(new PatternCondition<>("is first sequence item") {
150+
@Override
151+
public boolean accepts(@NotNull YAMLSequenceItem element, ProcessingContext context) {
152+
return element.getItemIndex() == 0;
153+
}
154+
})
155+
.withParent(
156+
PlatformPatterns
157+
.psiElement(YAMLSequence.class)
158+
.withParent(
159+
PlatformPatterns.psiElement(YAMLKeyValue.class).withName("factory", "configurator")
160+
)
161+
)
162+
),
163+
new YAMLScalarServiceReferenceProvider()
164+
);
165+
166+
// services:
167+
// app.service.foo: '@app.service.bar'
168+
registrar.registerReferenceProvider(
169+
PlatformPatterns
170+
.psiElement(YAMLScalar.class)
171+
.withParent(
172+
PlatformPatterns
173+
.psiElement(YAMLKeyValue.class)
174+
.withParent(
175+
PlatformPatterns
176+
.psiElement(YAMLMapping.class)
177+
.withParent(
178+
PlatformPatterns
179+
.psiElement(YAMLKeyValue.class)
180+
.withName("services")
181+
)
182+
)
183+
),
184+
new YAMLScalarServiceReferenceProvider()
185+
);
186+
187+
// services:
188+
// app.service.foo:
189+
// calls:
190+
// - setBar: [ '@app.service.bar' ]
191+
registrar.registerReferenceProvider(
192+
PlatformPatterns
193+
.psiElement(YAMLScalar.class)
194+
.withParent(
195+
PlatformPatterns
196+
.psiElement(YAMLSequenceItem.class)
197+
.withParent(
198+
PlatformPatterns
199+
.psiElement(YAMLSequence.class)
200+
.withParent(
201+
PlatformPatterns
202+
.psiElement(YAMLKeyValue.class)
203+
.withParent(
204+
PlatformPatterns
205+
.psiElement(YAMLMapping.class)
206+
.withParent(
207+
PlatformPatterns
208+
.psiElement(YAMLSequenceItem.class)
209+
.withParent(
210+
PlatformPatterns
211+
.psiElement(YAMLSequence.class)
212+
.withParent(
213+
PlatformPatterns
214+
.psiElement(YAMLKeyValue.class)
215+
.withName("calls")
216+
)
217+
)
218+
)
219+
)
220+
221+
)
222+
)
223+
),
224+
new YAMLScalarServiceReferenceProvider()
225+
);
226+
}
227+
228+
private static class YAMLScalarServiceReferenceProvider extends PsiReferenceProvider {
229+
230+
private static final String PREFIX = "@";
231+
private static final String ESCAPED_PREFIX = "@@";
232+
233+
/**
234+
* Flag indicating whenever YAMLScalar value start with `@` prefix
235+
*/
236+
private boolean isPrefixed = true;
237+
238+
public YAMLScalarServiceReferenceProvider() {
239+
}
240+
241+
public YAMLScalarServiceReferenceProvider(boolean isPrefixed) {
242+
this.isPrefixed = isPrefixed;
243+
}
244+
245+
@Override
246+
public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
247+
if (!Symfony2ProjectComponent.isEnabled(element)) {
248+
return PsiReference.EMPTY_ARRAY;
249+
}
250+
251+
if (element instanceof YAMLScalar) {
252+
var serviceName = ((YAMLScalar) element).getTextValue();
253+
if (serviceName.isEmpty()) {
254+
return PsiReference.EMPTY_ARRAY;
255+
}
256+
257+
if (!isPrefixed) {
258+
return new PsiReference[]{
259+
new ServiceYamlReference(element, serviceName)
260+
};
261+
}
262+
263+
if (isValidServiceNameWithPrefix(serviceName)) {
264+
var range = TextRange.from(serviceName.indexOf(PREFIX) + 1, serviceName.length() - 1);
265+
if (element instanceof YAMLQuotedText) {
266+
// Skip quotes
267+
range = range.shiftRight(1);
268+
}
269+
270+
return new PsiReference[]{
271+
new ServiceYamlReference(element, range, serviceName.substring(1))
272+
};
273+
}
274+
}
275+
276+
return PsiReference.EMPTY_ARRAY;
277+
}
278+
279+
private boolean isValidServiceNameWithPrefix(@NotNull String serviceName) {
280+
return serviceName.length() > 1 && serviceName.startsWith(PREFIX) && !serviceName.startsWith(ESCAPED_PREFIX);
281+
}
40282
}
41283
}

src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/AbstractServiceReference.java

+23-5
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
package fr.adrienbrault.idea.symfony2plugin.dic;
22

33
import com.intellij.codeInsight.lookup.LookupElement;
4+
import com.intellij.openapi.util.TextRange;
45
import com.intellij.psi.PsiElement;
56
import com.intellij.psi.PsiElementResolveResult;
67
import com.intellij.psi.PsiPolyVariantReferenceBase;
78
import com.intellij.psi.ResolveResult;
89
import com.intellij.util.containers.ContainerUtil;
910
import com.jetbrains.php.PhpIndex;
1011
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
12+
import fr.adrienbrault.idea.symfony2plugin.stubs.ServiceIndexUtil;
1113
import org.jetbrains.annotations.NotNull;
1214

1315
import java.util.ArrayList;
16+
import java.util.Arrays;
1417
import java.util.Collection;
1518
import java.util.List;
1619

@@ -26,19 +29,34 @@ public AbstractServiceReference(PsiElement psiElement) {
2629
super(psiElement);
2730
}
2831

29-
@NotNull
32+
public AbstractServiceReference(PsiElement psiElement, TextRange range) {
33+
super(psiElement, range);
34+
}
35+
3036
@Override
31-
public ResolveResult[] multiResolve(boolean incompleteCode) {
37+
public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) {
38+
var definitions = ServiceIndexUtil.findServiceDefinitions(
39+
getElement().getProject(),
40+
serviceId
41+
);
42+
43+
// Return the PsiElement for the service definition corresponding to the serviceId
44+
var results = new ArrayList<ResolveResult>();
45+
for (var definition : definitions) {
46+
results.add(new PsiElementResolveResult(definition));
47+
}
48+
3249
ContainerCollectionResolver.ServiceCollector collector = ContainerCollectionResolver
3350
.ServiceCollector.create(getElement().getProject());
3451

3552
// Return the PsiElement for the class corresponding to the serviceId
3653
String serviceClass = collector.resolve(serviceId);
37-
if (serviceClass == null) {
38-
return new ResolveResult[0];
54+
if (serviceClass != null) {
55+
var classes = PsiElementResolveResult.createResults(PhpIndex.getInstance(getElement().getProject()).getAnyByFQN(serviceClass));
56+
results.addAll(Arrays.asList(classes));
3957
}
4058

41-
return PsiElementResolveResult.createResults(PhpIndex.getInstance(getElement().getProject()).getAnyByFQN(serviceClass));
59+
return results.toArray(ResolveResult.EMPTY_ARRAY);
4260
}
4361

4462
@NotNull

0 commit comments

Comments
 (0)