Skip to content

Commit 5450061

Browse files
committed
Resolve resource service definitions via metadata in line markers
1 parent 25475bf commit 5450061

File tree

6 files changed

+127
-78
lines changed

6 files changed

+127
-78
lines changed

src/main/java/fr/adrienbrault/idea/symfony2plugin/config/ServiceLineMarkerProvider.java

Lines changed: 44 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import com.intellij.icons.AllIcons;
88
import com.intellij.openapi.project.Project;
99
import com.intellij.openapi.util.NotNullLazyValue;
10-
import com.intellij.openapi.util.Pair;
1110
import com.intellij.openapi.vfs.VirtualFile;
1211
import com.intellij.psi.PsiElement;
1312
import com.intellij.psi.PsiFile;
@@ -22,11 +21,11 @@
2221
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
2322
import fr.adrienbrault.idea.symfony2plugin.dic.ClassServiceDefinitionTargetLazyValue;
2423
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerService;
24+
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerServiceMetadata;
2525
import fr.adrienbrault.idea.symfony2plugin.dic.container.ServiceInterface;
2626
import fr.adrienbrault.idea.symfony2plugin.doctrine.metadata.util.DoctrineMetadataUtil;
2727
import fr.adrienbrault.idea.symfony2plugin.form.util.FormUtil;
2828
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
29-
import fr.adrienbrault.idea.symfony2plugin.stubs.ServiceIndexUtil;
3029
import fr.adrienbrault.idea.symfony2plugin.translation.ConstraintMessageGotoCompletionRegistrar;
3130
import fr.adrienbrault.idea.symfony2plugin.translation.dict.TranslationUtil;
3231
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
@@ -103,47 +102,37 @@ private void classNameMarker(@NotNull Project project, @NotNull PsiElement psiEl
103102
Icon serviceLineMarker = ExternalSystemIcons.Task;
104103
Collection<ClassServiceDefinitionTargetLazyValue> targets = new ArrayList<>();
105104
Collection<String> tags = new HashSet<>();
106-
107-
// a direct service match
108-
ClassServiceDefinitionTargetLazyValue psiElements = ServiceIndexUtil.findServiceDefinitionsLazy((PhpClass) phpClassContext);
109-
if (psiElements != null) {
110-
targets.add(psiElements);
111-
112-
// tags
113-
ContainerCollectionResolver.ServiceCollector serviceCollector = ContainerCollectionResolver.ServiceCollector.create(project);
114-
for (String convertClassNameToService : serviceCollector.convertClassNameToServices(((PhpClass) phpClassContext).getFQN())) {
115-
tags.addAll(ServiceUtil.getServiceTags(project, convertClassNameToService));
116-
117-
ContainerService containerService = serviceCollector.getServices().get(convertClassNameToService);
118-
if (containerService != null) {
119-
ServiceInterface service = containerService.getService();
120-
if (service != null) {
121-
tags.addAll(service.getTags());
122-
}
123-
}
124-
}
105+
ContainerCollectionResolver.ServiceCollector serviceCollector = ContainerCollectionResolver.ServiceCollector.create(project);
106+
Set<String> serviceNames = serviceCollector.convertClassNameToServices(((PhpClass) phpClassContext).getFQN());
107+
if (serviceNames.isEmpty()) {
108+
return;
125109
}
126110

127-
// via resource include
128-
Pair<ClassServiceDefinitionTargetLazyValue, Collection<ContainerService>> serviceDefinitionsOfResource = ServiceIndexUtil.findServiceDefinitionsOfResourceLazy((PhpClass) phpClassContext);
129-
if (serviceDefinitionsOfResource != null) {
111+
targets.add(new ClassServiceDefinitionTargetLazyValue(project, ((PhpClass) phpClassContext).getFQN()));
112+
113+
if (hasResourcePrototypeMetadata(serviceNames, serviceCollector)) {
130114
LayeredIcon serviceLineMarkerLayer = LayeredIcon.layeredIcon(new Icon[]{serviceLineMarker, AllIcons.Modules.SourceRootFileLayer});
131115
serviceLineMarkerLayer.setIcon(AllIcons.Modules.SourceRootFileLayer, 1, SwingConstants.CENTER);
132116

133117
serviceLineMarker = serviceLineMarkerLayer;
134-
targets.add(serviceDefinitionsOfResource.getFirst());
135-
136-
// tags
137-
for (ContainerService containerService : serviceDefinitionsOfResource.getSecond()) {
138-
ServiceInterface service = containerService.getService();
139-
if (service != null) {
140-
tags.addAll(ServiceUtil.getServiceTags(project, service.getId()));
141-
}
142-
}
143118
}
144119

145-
if (targets.isEmpty()) {
146-
return;
120+
for (String serviceName : serviceNames) {
121+
tags.addAll(ServiceUtil.getServiceTags(project, serviceName));
122+
123+
ContainerService containerService = serviceCollector.getServices().get(serviceName);
124+
if (containerService == null) {
125+
continue;
126+
}
127+
128+
ServiceInterface service = containerService.getService();
129+
if (service != null) {
130+
tags.addAll(service.getTags());
131+
}
132+
133+
for (ContainerServiceMetadata metadata : containerService.getMetadata()) {
134+
tags.addAll(metadata.tags());
135+
}
147136
}
148137

149138
if (!tags.isEmpty()) {
@@ -320,36 +309,17 @@ private void autowireConstructorMarker(@NotNull Project project, @NotNull PsiEle
320309
boolean isAutowire = false;
321310

322311
Collection<ClassServiceDefinitionTargetLazyValue> targets = new ArrayList<>();
323-
324-
Pair<ClassServiceDefinitionTargetLazyValue, Collection<ContainerService>> serviceDefinitionsOfResource = ServiceIndexUtil.findServiceDefinitionsOfResourceLazy(phpClass);
325-
if (serviceDefinitionsOfResource != null) {
326-
for (ContainerService containerService : serviceDefinitionsOfResource.getSecond()) {
327-
ServiceInterface service = containerService.getService();
328-
if (service == null) {
329-
continue;
330-
}
331-
332-
if (service.isAutowire()) {
333-
isAutowire = true;
334-
targets.add(serviceDefinitionsOfResource.getFirst());
335-
}
312+
ContainerCollectionResolver.ServiceCollector serviceCollector = ContainerCollectionResolver.ServiceCollector.create(project);
313+
for (String convertClassNameToService : serviceCollector.convertClassNameToServices(phpClass.getFQN())) {
314+
ContainerService containerService = serviceCollector.getServices().get(convertClassNameToService);
315+
if (containerService == null) {
316+
continue;
336317
}
337-
}
338318

339-
// direct service
340-
if (!isAutowire) {
341-
ContainerCollectionResolver.ServiceCollector serviceCollector = ContainerCollectionResolver.ServiceCollector.create(project);
342-
for (String convertClassNameToService : serviceCollector.convertClassNameToServices(phpClass.getFQN())) {
343-
ContainerService containerService = serviceCollector.getServices().get(convertClassNameToService);
344-
if (containerService == null) {
345-
continue;
346-
}
347-
348-
ServiceInterface service = containerService.getService();
349-
if (service != null && service.isAutowire()) {
350-
isAutowire = true;
351-
targets.add(new ClassServiceDefinitionTargetLazyValue(project, convertClassNameToService));
352-
}
319+
if (containerService.isAutowireEnabled()) {
320+
isAutowire = true;
321+
targets.add(new ClassServiceDefinitionTargetLazyValue(project, phpClass.getFQN()));
322+
break;
353323
}
354324
}
355325

@@ -364,6 +334,17 @@ private void autowireConstructorMarker(@NotNull Project project, @NotNull PsiEle
364334
results.add(builder.createLineMarkerInfo(psiElement));
365335
}
366336

337+
private static boolean hasResourcePrototypeMetadata(@NotNull Collection<String> serviceNames, @NotNull ContainerCollectionResolver.ServiceCollector serviceCollector) {
338+
for (String serviceName : serviceNames) {
339+
ContainerService containerService = serviceCollector.getServices().get(serviceName);
340+
if (containerService != null && containerService.hasResourcePrototypeMetadata()) {
341+
return true;
342+
}
343+
}
344+
345+
return false;
346+
}
347+
367348
private void constraintMessagePropertyMarker(@NotNull PsiElement psiElement, @NotNull Collection<? super LineMarkerInfo<?>> results) {
368349
PsiElement parent = psiElement.getParent();
369350
if (parent instanceof StringLiteralExpression && TranslationUtil.isConstraintPropertyField((StringLiteralExpression) parent)) {
@@ -395,4 +376,3 @@ public Collection<? extends PsiElement> get() {
395376
}
396377
}
397378
}
398-

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ public Collection<? extends PsiElement> get() {
4646

4747
for (String serviceName: serviceNames) {
4848
psiElements.addAll(ServiceIndexUtil.findServiceDefinitions(project, serviceName));
49+
50+
// A class can point to both its direct service id and one or more resource/prototype ids.
51+
ContainerService containerService = serviceCollector.getServices().get(serviceName);
52+
if (containerService != null) {
53+
for (String resourceServiceId : containerService.getResourceServiceIds()) {
54+
psiElements.addAll(ServiceIndexUtil.findServiceDefinitions(project, resourceServiceId));
55+
}
56+
}
4957
}
5058
}
5159

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

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ public class ContainerService {
3030
private Set<String> cachedClassNames;
3131
@Nullable
3232
private Boolean cachedMetadataAutowire;
33+
@Nullable
34+
private Boolean cachedResourcePrototypeMetadata;
35+
@Nullable
36+
private Set<String> cachedResourceServiceIds;
3337

3438
public ContainerService(@NotNull ServiceInterface service, @Nullable String classResolved) {
3539
this.service = new MemoryReducedCollectionService(service);
@@ -71,6 +75,8 @@ public void addClassName(@NotNull String className) {
7175
public void addMetadata(@NotNull ContainerServiceMetadata metadata) {
7276
if (this.metadata.add(metadata)) {
7377
this.cachedMetadataAutowire = null;
78+
this.cachedResourcePrototypeMetadata = null;
79+
this.cachedResourceServiceIds = null;
7480
}
7581
}
7682

@@ -133,6 +139,37 @@ public boolean isAutowireEnabled() {
133139
return cachedMetadataAutowire = false;
134140
}
135141

142+
public boolean hasResourcePrototypeMetadata() {
143+
if (cachedResourcePrototypeMetadata != null) {
144+
return cachedResourcePrototypeMetadata;
145+
}
146+
147+
for (ContainerServiceMetadata containerServiceMetadata : metadata) {
148+
if (containerServiceMetadata.sourceKind() == ContainerServiceMetadata.SourceKind.RESOURCE_PROTOTYPE) {
149+
return cachedResourcePrototypeMetadata = true;
150+
}
151+
}
152+
153+
return cachedResourcePrototypeMetadata = false;
154+
}
155+
156+
@NotNull
157+
public Set<String> getResourceServiceIds() {
158+
if (cachedResourceServiceIds != null) {
159+
return cachedResourceServiceIds;
160+
}
161+
162+
Set<String> resourceServiceIds = new LinkedHashSet<>();
163+
for (ContainerServiceMetadata containerServiceMetadata : metadata) {
164+
String resourceServiceId = containerServiceMetadata.resourceServiceId();
165+
if (resourceServiceId != null) {
166+
resourceServiceIds.add(resourceServiceId);
167+
}
168+
}
169+
170+
return cachedResourceServiceIds = Collections.unmodifiableSet(resourceServiceIds);
171+
}
172+
136173
/**
137174
* legacy support
138175
*/

src/main/java/fr/adrienbrault/idea/symfony2plugin/util/dict/ServiceUtil.java

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import com.intellij.openapi.ui.popup.JBPopupFactory;
99
import com.intellij.openapi.util.Key;
1010
import com.intellij.openapi.util.NotNullLazyValue;
11-
import com.intellij.openapi.util.Pair;
1211
import com.intellij.openapi.vfs.VirtualFile;
1312
import com.intellij.psi.PsiElement;
1413
import com.intellij.psi.PsiFile;
@@ -30,7 +29,6 @@
3029
import fr.adrienbrault.idea.symfony2plugin.action.generator.naming.JavascriptServiceNameStrategy;
3130
import fr.adrienbrault.idea.symfony2plugin.action.generator.naming.ServiceNameStrategyInterface;
3231
import fr.adrienbrault.idea.symfony2plugin.action.generator.naming.ServiceNameStrategyParameter;
33-
import fr.adrienbrault.idea.symfony2plugin.dic.ClassServiceDefinitionTargetLazyValue;
3432
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerService;
3533
import fr.adrienbrault.idea.symfony2plugin.dic.XmlTagParser;
3634
import fr.adrienbrault.idea.symfony2plugin.form.util.FormUtil;
@@ -741,16 +739,8 @@ public static RelatedItemLineMarkerInfo<PsiElement> getLineMarkerForDecoratesSer
741739
}
742740

743741
public static boolean isPhpClassAService(@NotNull PhpClass phpClass) {
744-
Set<String> serviceNames = ContainerCollectionResolver.ServiceCollector.create(phpClass.getProject()).convertClassNameToServices(phpClass.getFQN());
745-
if (!serviceNames.isEmpty()) {
746-
return true;
747-
}
748-
749-
Pair<ClassServiceDefinitionTargetLazyValue, Collection<ContainerService>> serviceDefinitionsOfResource = ServiceIndexUtil.findServiceDefinitionsOfResourceLazy(phpClass);
750-
if (serviceDefinitionsOfResource != null) {
751-
return true;
752-
}
753-
754-
return false;
742+
return !ContainerCollectionResolver.ServiceCollector.create(phpClass.getProject())
743+
.convertClassNameToServices(phpClass.getFQN())
744+
.isEmpty();
755745
}
756746
}

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/ServiceLineMarkerProviderTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.jetbrains.php.lang.PhpFileType;
99
import com.jetbrains.php.lang.psi.PhpPsiElementFactory;
1010
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
11+
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
1112
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;
1213
import org.jetbrains.annotations.NotNull;
1314
import org.jetbrains.yaml.YAMLFileType;
@@ -247,6 +248,26 @@ public void testThatPhpArrayResourceAutowireConstructorIsGivenALineMarker() {
247248
));
248249
}
249250

251+
public void testThatYamlResourceClassIsGivenADefinitionLineMarker() {
252+
var phpFile = myFixture.addFileToProject("Service/ResourceFooService.php", "<?php\n" +
253+
"namespace App\\Service {\n" +
254+
" class ResourceFooService {}\n" +
255+
"}\n"
256+
);
257+
myFixture.configureFromExistingVirtualFile(myFixture.addFileToProject("config/services.yml",
258+
"services:\n" +
259+
" _defaults:\n" +
260+
" autowire: true\n" +
261+
" App\\Service\\:\n" +
262+
" resource: ../Service/*\n"
263+
).getVirtualFile());
264+
265+
assertTrue(ContainerCollectionResolver.hasServiceNames(getProject(), "App\\Service\\ResourceFooService"));
266+
267+
myFixture.configureFromExistingVirtualFile(phpFile.getVirtualFile());
268+
assertLineMarker(myFixture.getFile(), new LineMarker.ToolTipEqualsAssert("Navigate to definition"));
269+
}
270+
250271
public void testNavigateToTranslationForConstraintMessage() {
251272
assertLineMarker(myFixture.configureByText(PhpFileType.INSTANCE, "<?php\n" +
252273
"class LessThan extends \\Symfony\\Component\\Validator\\Constraint\n" +

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/stubs/ContainerCollectionResolverTest.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package fr.adrienbrault.idea.symfony2plugin.tests.stubs;
22

3+
import com.intellij.openapi.vfs.VirtualFile;
4+
import com.intellij.psi.PsiElement;
5+
import com.intellij.psi.PsiFile;
6+
import com.intellij.psi.PsiManager;
7+
import fr.adrienbrault.idea.symfony2plugin.dic.ClassServiceDefinitionTargetLazyValue;
38
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerParameter;
49
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerService;
510
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerServiceMetadata;
@@ -8,6 +13,7 @@
813
import org.jetbrains.annotations.NotNull;
914
import org.jetbrains.yaml.YAMLFileType;
1015

16+
import java.util.Collection;
1117
import java.util.Objects;
1218
import java.util.Set;
1319

@@ -138,7 +144,9 @@ public void testThatGetKernelParametersAreCollected() {
138144

139145
public void testThatResourceBasedServicesAreResolved() {
140146
myFixture.copyFileToProject("ResourceFooService.php", "Service/ResourceFooService.php");
141-
myFixture.copyFileToProject("resource_based_services.yml", "config/services.yml");
147+
VirtualFile virtualFile = myFixture.copyFileToProject("resource_based_services.yml", "config/services.yml");
148+
PsiFile psiFile = PsiManager.getInstance(getProject()).findFile(virtualFile);
149+
assertNotNull(psiFile);
142150

143151
assertTrue(ContainerCollectionResolver.hasServiceNames(getProject(), "App\\Service\\ResourceFooService"));
144152

@@ -149,6 +157,11 @@ public void testThatResourceBasedServicesAreResolved() {
149157
assertTrue(metadata.autoconfigure());
150158
assertContainsElements(metadata.resource(), "../Service/*");
151159
assertEmpty(metadata.exclude());
160+
161+
Collection<? extends PsiElement> targets = new ClassServiceDefinitionTargetLazyValue(getProject(), "\\App\\Service\\ResourceFooService").get();
162+
assertTrue(targets.stream().anyMatch(target ->
163+
psiFile.equals(target.getContainingFile()) && target.getText().contains("App\\Service\\")
164+
));
152165
}
153166

154167
public void testThatResourceBasedServiceWithExcludeAttributeIsFiltered() {

0 commit comments

Comments
 (0)