Skip to content

Commit a861cb0

Browse files
committed
Added service name reference contributor for YAML DIC files
1 parent 13ac25b commit a861cb0

File tree

4 files changed

+426
-6
lines changed

4 files changed

+426
-6
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package fr.adrienbrault.idea.symfony2plugin.config;
2+
3+
import com.intellij.psi.PsiElement;
4+
import com.intellij.psi.PsiElementResolveResult;
5+
import com.intellij.psi.PsiPolyVariantReferenceBase;
6+
import com.intellij.psi.ResolveResult;
7+
import com.intellij.util.IncorrectOperationException;
8+
import fr.adrienbrault.idea.symfony2plugin.stubs.ServiceIndexUtil;
9+
import org.jetbrains.annotations.NotNull;
10+
11+
import java.util.ArrayList;
12+
13+
public class ServiceReference extends PsiPolyVariantReferenceBase<PsiElement> {
14+
15+
private static final String REFERENCE_PREFIX = "@";
16+
17+
@NotNull
18+
private final String serviceName;
19+
20+
public ServiceReference(@NotNull PsiElement psiElement, @NotNull String serviceName) {
21+
super(psiElement);
22+
23+
this.serviceName = serviceName;
24+
}
25+
26+
@Override
27+
public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) {
28+
var definitions = ServiceIndexUtil.findServiceDefinitions(
29+
getElement().getProject(),
30+
getNormalizedServiceName(serviceName)
31+
);
32+
33+
var results = new ArrayList<ResolveResult>();
34+
for (var definition : definitions) {
35+
results.add(new PsiElementResolveResult(definition));
36+
}
37+
38+
return results.toArray(ResolveResult.EMPTY_ARRAY);
39+
}
40+
41+
@Override
42+
public PsiElement handleElementRename(@NotNull String newElementName) throws IncorrectOperationException {
43+
if (serviceName.startsWith(REFERENCE_PREFIX)) {
44+
newElementName = REFERENCE_PREFIX + newElementName;
45+
}
46+
47+
return super.handleElementRename(newElementName);
48+
}
49+
50+
@NotNull
51+
private String getNormalizedServiceName(@NotNull String serviceName) {
52+
if (serviceName.startsWith(REFERENCE_PREFIX)) {
53+
// Remove '@' prefix
54+
serviceName = serviceName.substring(1);
55+
}
56+
57+
return serviceName;
58+
}
59+
}

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

Lines changed: 179 additions & 2 deletions
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.patterns.PatternCondition;
34
import com.intellij.patterns.PlatformPatterns;
45
import com.intellij.patterns.StandardPatterns;
56
import com.intellij.psi.*;
67
import com.intellij.util.ProcessingContext;
78
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
9+
import fr.adrienbrault.idea.symfony2plugin.config.ServiceReference;
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,180 @@ 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+
167+
private static class YAMLScalarServiceReferenceProvider extends PsiReferenceProvider {
168+
169+
private static final String PREFIX = "@";
170+
private static final String ESCAPED_PREFIX = "@@";
171+
172+
/**
173+
* Flag indicating whenever YAMLScalar value start with `@` prefix
174+
*/
175+
private boolean isPrefixed = true;
176+
177+
public YAMLScalarServiceReferenceProvider() {
178+
}
179+
180+
public YAMLScalarServiceReferenceProvider(boolean isPrefixed) {
181+
this.isPrefixed = isPrefixed;
182+
}
183+
184+
@Override
185+
public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element, @NotNull ProcessingContext context) {
186+
if (!Symfony2ProjectComponent.isEnabled(element)) {
187+
return PsiReference.EMPTY_ARRAY;
188+
}
189+
190+
if (element instanceof YAMLScalar) {
191+
var serviceName = ((YAMLScalar) element).getTextValue();
192+
if (serviceName.isEmpty()) {
193+
return PsiReference.EMPTY_ARRAY;
194+
}
195+
196+
if (isPrefixed) {
197+
if (isValidServiceNameWithPrefix(serviceName)) {
198+
return new PsiReference[]{
199+
new ServiceReference(element, serviceName)
200+
};
201+
}
202+
203+
return PsiReference.EMPTY_ARRAY;
204+
}
205+
206+
return new PsiReference[]{
207+
new ServiceReference(element, serviceName)
208+
};
209+
}
210+
211+
return PsiReference.EMPTY_ARRAY;
212+
}
213+
214+
private boolean isValidServiceNameWithPrefix(@NotNull String serviceName) {
215+
return serviceName.length() > 1 && serviceName.startsWith(PREFIX) && !serviceName.startsWith(ESCAPED_PREFIX);
216+
}
40217
}
41218
}

0 commit comments

Comments
 (0)