Skip to content

Commit 0bbf736

Browse files
Aleksandr SlapoguzovAleksandr Slapoguzov
authored andcommitted
Merge branch 'master' into 202-support
# Conflicts: # src/main/java/fr/adrienbrault/idea/symfony2plugin/config/ServiceLineMarkerProvider.java
2 parents fa87f78 + 82bc31c commit 0bbf736

File tree

44 files changed

+1069
-58
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1069
-58
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ Changelog
1515
* 0.10.x: PhpStorm 7 (no support)
1616
* 0.9.x: PhpStorm 6 (no support)
1717

18+
## 0.20.197
19+
* Provide checkbox to disable Twig file icon decoration [#1485](https://github.com/Haehnchen/idea-php-symfony2-plugin/issues/1485) (Daniel Espendiller)
20+
* Enrich compile service data with metadata indexer to support configuration like autowire (Daniel Espendiller)
21+
* Use internal icons for service linemarker (Daniel Espendiller)
22+
* Provide linemarker for a constructor which supports autowire (Daniel Espendiller)
23+
* Provide yaml navigation for services defined via resource (Daniel Espendiller)
24+
* Provide resources index for service and use it in linemarker classes to indicate it and also provide a tagged icon (Daniel Espendiller)
25+
26+
## 0.20.196
27+
* Ignore Doctrine repository return type provider on magic method pattern if already in repository [#1481](https://github.com/Haehnchen/idea-php-symfony2-plugin/issues/1481) (Daniel Espendiller)
28+
* Provide custom Twig file overlay to indicate "extends" and attached controller template types [#1485](https://github.com/Haehnchen/idea-php-symfony2-plugin/issues/1485) (Daniel Espendiller)
29+
* Index service definition for PHP files (Daniel Espendiller)
30+
* Support global Symfony DIC function: "service" and "param" (Daniel Espendiller)
31+
1832
## 0.20.195
1933
* Fix possible long signature truncate and split on type resolver for repository find* (Daniel Espendiller)
2034

src/main/java/fr/adrienbrault/idea/symfony2plugin/Settings.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerFile;
1212
import fr.adrienbrault.idea.symfony2plugin.routing.dict.RoutingFile;
1313
import fr.adrienbrault.idea.symfony2plugin.templating.path.TwigNamespaceSetting;
14+
import org.jetbrains.annotations.NotNull;
1415
import org.jetbrains.annotations.Nullable;
1516

1617
import java.util.ArrayList;
@@ -55,6 +56,7 @@ public class Settings implements PersistentStateComponent<Settings> {
5556
public boolean codeFoldingTwigRoute = true;
5657
public boolean codeFoldingTwigTemplate = true;
5758
public boolean codeFoldingTwigConstant = true;
59+
public boolean featureTwigIcon = true;
5860

5961
public boolean twigBundleNamespaceSupport = true;
6062

@@ -103,7 +105,7 @@ public Settings getState() {
103105
}
104106

105107
@Override
106-
public void loadState(Settings settings) {
108+
public void loadState(@NotNull Settings settings) {
107109
XmlSerializerUtil.copyBean(settings, this);
108110
}
109111
}

src/main/java/fr/adrienbrault/idea/symfony2plugin/SettingsForm.form

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
<rowspec value="center:max(d;4px):noGrow"/>
3333
<rowspec value="top:4dlu:noGrow"/>
3434
<rowspec value="center:max(d;4px):noGrow"/>
35+
<rowspec value="top:4dlu:noGrow"/>
36+
<rowspec value="center:max(d;4px):noGrow"/>
37+
<rowspec value="top:4dlu:noGrow"/>
38+
<rowspec value="center:max(d;4px):noGrow"/>
3539
<colspec value="fill:max(d;4px):noGrow"/>
3640
<colspec value="left:4dlu:noGrow"/>
3741
<colspec value="fill:d:grow"/>
@@ -207,6 +211,25 @@
207211
<text value="Download remote files (exp.)"/>
208212
</properties>
209213
</component>
214+
<component id="24207" class="javax.swing.JLabel">
215+
<constraints>
216+
<grid row="24" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
217+
<forms/>
218+
</constraints>
219+
<properties>
220+
<text value="Custom Features"/>
221+
</properties>
222+
</component>
223+
<component id="bcf9" class="javax.swing.JCheckBox" binding="featureTwigIcon">
224+
<constraints>
225+
<grid row="26" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
226+
<forms/>
227+
</constraints>
228+
<properties>
229+
<text value="Twig Icon Decoration"/>
230+
<toolTipText value="Decorate Twig file icons with layer to show possible content type"/>
231+
</properties>
232+
</component>
210233
</children>
211234
</grid>
212235
<component id="2ef99" class="javax.swing.JCheckBox" binding="pluginEnabled">

src/main/java/fr/adrienbrault/idea/symfony2plugin/SettingsForm.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public class SettingsForm implements Configurable {
5656

5757
private JButton buttonReindex;
5858
private JCheckBox enableSchedulerCheckBox;
59+
private JCheckBox featureTwigIcon;
5960

6061
public SettingsForm(@NotNull final Project project) {
6162
this.project = project;
@@ -116,6 +117,7 @@ public boolean isModified() {
116117
|| !codeFoldingTwigRoute.isSelected() == getSettings().codeFoldingTwigRoute
117118
|| !codeFoldingTwigTemplate.isSelected() == getSettings().codeFoldingTwigTemplate
118119
|| !codeFoldingTwigConstant.isSelected() == getSettings().codeFoldingTwigConstant
120+
|| !featureTwigIcon.isSelected() == getSettings().featureTwigIcon
119121

120122
|| !directoryToApp.getText().equals(getSettings().directoryToApp)
121123
|| !directoryToWeb.getText().equals(getSettings().directoryToWeb)
@@ -136,6 +138,7 @@ public void apply() throws ConfigurationException {
136138
getSettings().codeFoldingTwigRoute = codeFoldingTwigRoute.isSelected();
137139
getSettings().codeFoldingTwigTemplate = codeFoldingTwigTemplate.isSelected();
138140
getSettings().codeFoldingTwigConstant = codeFoldingTwigConstant.isSelected();
141+
getSettings().featureTwigIcon = featureTwigIcon.isSelected();
139142

140143
getSettings().directoryToApp = directoryToApp.getText();
141144
getSettings().directoryToWeb = directoryToWeb.getText();
@@ -167,6 +170,7 @@ private void updateUIFromSettings() {
167170
codeFoldingTwigRoute.setSelected(getSettings().codeFoldingTwigRoute);
168171
codeFoldingTwigTemplate.setSelected(getSettings().codeFoldingTwigTemplate);
169172
codeFoldingTwigConstant.setSelected(getSettings().codeFoldingTwigConstant);
173+
featureTwigIcon.setSelected(getSettings().featureTwigIcon);
170174

171175
directoryToApp.setText(getSettings().directoryToApp);
172176
directoryToWeb.setText(getSettings().directoryToWeb);

src/main/java/fr/adrienbrault/idea/symfony2plugin/Symfony2Icons.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public class Symfony2Icons {
6161
public static final Icon TWIG_LINE_MARKER = IconLoader.getIcon("/icons/twig_line_marker.png");
6262
public static final Icon TWIG_LINE_OVERWRITE = IconLoader.getIcon("/icons/overwrite.png");
6363

64+
public static final Icon TWIG_CONTROLLER_FILE = IconLoader.getIcon("/icons/twig_controller_file.png");
6465
public static final Icon TWIG_EXTENDS_FILE = IconLoader.getIcon("/icons/twig_extends_file.png");
6566
public static final Icon TWIG_IMPLEMENTS_FILE = IconLoader.getIcon("/icons/twig_implements_file.png");
6667

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

Lines changed: 137 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,43 @@
44
import com.intellij.codeInsight.daemon.LineMarkerProvider;
55
import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo;
66
import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder;
7+
import com.intellij.icons.AllIcons;
78
import com.intellij.openapi.project.Project;
9+
import com.intellij.openapi.util.NotNullLazyValue;
10+
import com.intellij.openapi.util.Pair;
811
import com.intellij.openapi.vfs.VirtualFile;
912
import com.intellij.psi.PsiElement;
1013
import com.intellij.psi.PsiFile;
1114
import com.intellij.psi.PsiManager;
1215
import com.intellij.psi.util.PsiTreeUtil;
16+
import com.intellij.ui.LayeredIcon;
1317
import com.jetbrains.php.lang.psi.PhpFile;
1418
import com.jetbrains.php.lang.psi.elements.Method;
1519
import com.jetbrains.php.lang.psi.elements.PhpClass;
1620
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
1721
import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons;
1822
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
1923
import fr.adrienbrault.idea.symfony2plugin.dic.ClassServiceDefinitionTargetLazyValue;
24+
import fr.adrienbrault.idea.symfony2plugin.dic.ContainerService;
25+
import fr.adrienbrault.idea.symfony2plugin.dic.container.ServiceInterface;
2026
import fr.adrienbrault.idea.symfony2plugin.doctrine.EntityHelper;
2127
import fr.adrienbrault.idea.symfony2plugin.doctrine.metadata.util.DoctrineMetadataUtil;
2228
import fr.adrienbrault.idea.symfony2plugin.form.util.FormUtil;
29+
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
2330
import fr.adrienbrault.idea.symfony2plugin.stubs.ServiceIndexUtil;
2431
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
2532
import fr.adrienbrault.idea.symfony2plugin.util.dict.DoctrineModel;
33+
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil;
2634
import fr.adrienbrault.idea.symfony2plugin.util.resource.FileResourceUtil;
35+
import icons.ExternalSystemIcons;
2736
import org.apache.commons.lang.StringUtils;
2837
import org.jetbrains.annotations.NotNull;
2938
import org.jetbrains.annotations.Nullable;
3039

40+
import javax.swing.*;
3141
import java.util.ArrayList;
3242
import java.util.Collection;
43+
import java.util.HashSet;
3344
import java.util.List;
3445

3546
/**
@@ -70,6 +81,10 @@ public void collectSlowLineMarkers(@NotNull List<? extends PsiElement> psiElemen
7081
this.constraintValidatorClassMarker(psiElement, results);
7182
}
7283

84+
if(PhpElementsUtil.getClassMethodNamePattern().accepts(psiElement)) {
85+
this.autowireConstructorMarker(psiElement, results);
86+
}
87+
7388
if(psiElement instanceof PhpFile) {
7489
routeAnnotationFileResource((PhpFile) psiElement, results);
7590
}
@@ -78,23 +93,61 @@ public void collectSlowLineMarkers(@NotNull List<? extends PsiElement> psiElemen
7893
}
7994

8095
private void classNameMarker(PsiElement psiElement, Collection<? super RelatedItemLineMarkerInfo<?>> result) {
81-
8296
PsiElement phpClassContext = psiElement.getContext();
8397
if(!(phpClassContext instanceof PhpClass)) {
8498
return;
8599
}
86100

101+
Icon serviceLineMarker = ExternalSystemIcons.Task;
102+
Collection<ClassServiceDefinitionTargetLazyValue> targets = new ArrayList<>();
103+
Collection<String> tags = new HashSet<>();
104+
105+
// a direct service match
87106
ClassServiceDefinitionTargetLazyValue psiElements = ServiceIndexUtil.findServiceDefinitionsLazy((PhpClass) phpClassContext);
88-
if(psiElements == null) {
107+
if (psiElements != null) {
108+
targets.add(psiElements);
109+
110+
// tags
111+
ContainerCollectionResolver.ServiceCollector serviceCollector = ContainerCollectionResolver.ServiceCollector.create(psiElement.getProject());
112+
for (String convertClassNameToService : serviceCollector.convertClassNameToServices(((PhpClass) phpClassContext).getFQN())) {
113+
tags.addAll(ServiceUtil.getServiceTags(phpClassContext.getProject(), convertClassNameToService));
114+
}
115+
}
116+
117+
// via resource include
118+
Pair<ClassServiceDefinitionTargetLazyValue, Collection<ContainerService>> serviceDefinitionsOfResource = ServiceIndexUtil.findServiceDefinitionsOfResourceLazy((PhpClass) phpClassContext);
119+
if (serviceDefinitionsOfResource != null) {
120+
LayeredIcon serviceLineMarkerLayer = new LayeredIcon(serviceLineMarker, AllIcons.Modules.SourceRootFileLayer);
121+
serviceLineMarkerLayer.setIcon(AllIcons.Modules.SourceRootFileLayer, 1, SwingConstants.CENTER);
122+
123+
serviceLineMarker = serviceLineMarkerLayer;
124+
targets.add(serviceDefinitionsOfResource.getFirst());
125+
126+
// tags
127+
for (ContainerService containerService : serviceDefinitionsOfResource.getSecond()) {
128+
ServiceInterface service = containerService.getService();
129+
if (service != null) {
130+
tags.addAll(ServiceUtil.getServiceTags(phpClassContext.getProject(), service.getId()));
131+
}
132+
}
133+
}
134+
135+
if (targets.isEmpty()) {
89136
return;
90137
}
91138

92-
NavigationGutterIconBuilder<PsiElement> builder = NavigationGutterIconBuilder.create(Symfony2Icons.SERVICE_LINE_MARKER).
93-
setTargets(psiElements).
94-
setTooltipText("Navigate to definition");
139+
if (!tags.isEmpty()) {
140+
LayeredIcon serviceLineMarkerLayer = new LayeredIcon(serviceLineMarker, AllIcons.Nodes.TabPin);
141+
serviceLineMarkerLayer.setIcon(AllIcons.Nodes.TabPin, 1, SwingConstants.CENTER);
95142

96-
result.add(builder.createLineMarkerInfo(psiElement));
143+
serviceLineMarker = serviceLineMarkerLayer;
144+
}
145+
146+
NavigationGutterIconBuilder<PsiElement> builder = NavigationGutterIconBuilder.create(serviceLineMarker)
147+
.setTargets(new MyCollectionNotNullLazyValue(targets))
148+
.setTooltipText("Navigate to definition");
97149

150+
result.add(builder.createLineMarkerInfo(psiElement));
98151
}
99152

100153
private void entityClassMarker(PsiElement psiElement, Collection<? super RelatedItemLineMarkerInfo<?>> result) {
@@ -254,5 +307,83 @@ private void constraintValidatorClassMarker(PsiElement psiElement, Collection<?
254307

255308
results.add(builder.createLineMarkerInfo(psiElement));
256309
}
310+
311+
private void autowireConstructorMarker(PsiElement psiElement, Collection<? super LineMarkerInfo<?>> results) {
312+
PsiElement method = psiElement.getParent();
313+
if (!(method instanceof Method)) {
314+
return;
315+
}
316+
317+
if (!"__construct".equals(((Method) method).getName()) || !((Method) method).getAccess().isPublic()) {
318+
return;
319+
}
320+
321+
PhpClass phpClass = ((Method) method).getContainingClass();
322+
if (phpClass == null) {
323+
return;
324+
}
325+
326+
boolean isAutowire = false;
327+
328+
Collection<ClassServiceDefinitionTargetLazyValue> targets = new ArrayList<>();
329+
330+
Pair<ClassServiceDefinitionTargetLazyValue, Collection<ContainerService>> serviceDefinitionsOfResource = ServiceIndexUtil.findServiceDefinitionsOfResourceLazy(phpClass);
331+
if (serviceDefinitionsOfResource != null) {
332+
for (ContainerService containerService : serviceDefinitionsOfResource.getSecond()) {
333+
ServiceInterface service = containerService.getService();
334+
if (service == null) {
335+
continue;
336+
}
337+
338+
if (service.isAutowire()) {
339+
isAutowire = true;
340+
targets.add(serviceDefinitionsOfResource.getFirst());
341+
}
342+
}
343+
}
344+
345+
// direct service
346+
if (!isAutowire) {
347+
ContainerCollectionResolver.ServiceCollector serviceCollector = ContainerCollectionResolver.ServiceCollector.create(phpClass.getProject());
348+
for (String convertClassNameToService : serviceCollector.convertClassNameToServices(phpClass.getFQN())) {
349+
ContainerService containerService = serviceCollector.getServices().get(convertClassNameToService);
350+
if (containerService == null) {
351+
continue;
352+
}
353+
354+
ServiceInterface service = containerService.getService();
355+
if (service != null && service.isAutowire()) {
356+
isAutowire = true;
357+
targets.add(new ClassServiceDefinitionTargetLazyValue(phpClass.getProject(), convertClassNameToService));
358+
}
359+
}
360+
}
361+
362+
if (!isAutowire) {
363+
return;
364+
}
365+
366+
NavigationGutterIconBuilder<PsiElement> builder = NavigationGutterIconBuilder.create(AllIcons.Nodes.Plugin)
367+
.setTargets(new MyCollectionNotNullLazyValue(targets))
368+
.setTooltipText("Symfony: <a href=\"https://symfony.com/doc/current/service_container/autowiring.html\">Autowire available</a>");
369+
370+
results.add(builder.createLineMarkerInfo(psiElement));
371+
}
372+
373+
private static class MyCollectionNotNullLazyValue extends NotNullLazyValue<Collection<? extends PsiElement>> {
374+
private final Collection<ClassServiceDefinitionTargetLazyValue> targets;
375+
376+
public MyCollectionNotNullLazyValue(@NotNull Collection<ClassServiceDefinitionTargetLazyValue> targets) {
377+
this.targets = targets;
378+
}
379+
380+
@NotNull
381+
@Override
382+
protected Collection<? extends PsiElement> compute() {
383+
Collection<PsiElement> myTargets = new HashSet<>();
384+
targets.stream().map(NotNullLazyValue::getValue).forEach(myTargets::addAll);
385+
return myTargets;
386+
}
387+
}
257388
}
258389

src/main/java/fr/adrienbrault/idea/symfony2plugin/config/php/PhpConfigReferenceContributor.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,22 @@ public PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement, @No
8484
}
8585
);
8686

87+
// service('<caret>'), ref('<caret>') (ref is deprecated)
88+
psiReferenceRegistrar.registerReferenceProvider(
89+
PhpElementsUtil.getFunctionWithFirstStringPattern("service", "ref"),
90+
new PsiReferenceProvider() {
91+
@NotNull
92+
@Override
93+
public PsiReference[] getReferencesByElement(@NotNull PsiElement psiElement, @NotNull ProcessingContext processingContext) {
94+
if (!Symfony2ProjectComponent.isEnabled(psiElement)) {
95+
return new PsiReference[0];
96+
}
97+
98+
return new PsiReference[]{ new ServiceReference((StringLiteralExpression) psiElement, true) };
99+
}
100+
}
101+
);
102+
87103
psiReferenceRegistrar.registerReferenceProvider(
88104
PlatformPatterns.psiElement(StringLiteralExpression.class).withLanguage(PhpLanguage.INSTANCE),
89105
new PsiReferenceProvider() {

0 commit comments

Comments
 (0)