44import com .intellij .codeInsight .daemon .LineMarkerProvider ;
55import com .intellij .codeInsight .daemon .RelatedItemLineMarkerInfo ;
66import com .intellij .codeInsight .navigation .NavigationGutterIconBuilder ;
7+ import com .intellij .icons .AllIcons ;
78import com .intellij .openapi .project .Project ;
9+ import com .intellij .openapi .util .NotNullLazyValue ;
10+ import com .intellij .openapi .util .Pair ;
811import com .intellij .openapi .vfs .VirtualFile ;
912import com .intellij .psi .PsiElement ;
1013import com .intellij .psi .PsiFile ;
1114import com .intellij .psi .PsiManager ;
1215import com .intellij .psi .util .PsiTreeUtil ;
16+ import com .intellij .ui .LayeredIcon ;
1317import com .jetbrains .php .lang .psi .PhpFile ;
1418import com .jetbrains .php .lang .psi .elements .Method ;
1519import com .jetbrains .php .lang .psi .elements .PhpClass ;
1620import com .jetbrains .php .lang .psi .elements .StringLiteralExpression ;
1721import fr .adrienbrault .idea .symfony2plugin .Symfony2Icons ;
1822import fr .adrienbrault .idea .symfony2plugin .Symfony2ProjectComponent ;
1923import fr .adrienbrault .idea .symfony2plugin .dic .ClassServiceDefinitionTargetLazyValue ;
24+ import fr .adrienbrault .idea .symfony2plugin .dic .ContainerService ;
25+ import fr .adrienbrault .idea .symfony2plugin .dic .container .ServiceInterface ;
2026import fr .adrienbrault .idea .symfony2plugin .doctrine .EntityHelper ;
2127import fr .adrienbrault .idea .symfony2plugin .doctrine .metadata .util .DoctrineMetadataUtil ;
2228import fr .adrienbrault .idea .symfony2plugin .form .util .FormUtil ;
29+ import fr .adrienbrault .idea .symfony2plugin .stubs .ContainerCollectionResolver ;
2330import fr .adrienbrault .idea .symfony2plugin .stubs .ServiceIndexUtil ;
2431import fr .adrienbrault .idea .symfony2plugin .util .PhpElementsUtil ;
2532import fr .adrienbrault .idea .symfony2plugin .util .dict .DoctrineModel ;
33+ import fr .adrienbrault .idea .symfony2plugin .util .dict .ServiceUtil ;
2634import fr .adrienbrault .idea .symfony2plugin .util .resource .FileResourceUtil ;
35+ import icons .ExternalSystemIcons ;
2736import org .apache .commons .lang .StringUtils ;
2837import org .jetbrains .annotations .NotNull ;
2938import org .jetbrains .annotations .Nullable ;
3039
40+ import javax .swing .*;
3141import java .util .ArrayList ;
3242import java .util .Collection ;
43+ import java .util .HashSet ;
3344import 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
0 commit comments