55import com .intellij .patterns .PlatformPatterns ;
66import com .intellij .psi .PsiElement ;
77import com .intellij .psi .util .PsiTreeUtil ;
8- import com .jetbrains .php .lang .psi .elements .PhpAttribute ;
9- import com .jetbrains .php .lang .psi .elements .PhpClass ;
10- import com .jetbrains .php .lang .psi .elements .StringLiteralExpression ;
8+ import com .jetbrains .php .lang .psi .elements .*;
119import fr .adrienbrault .idea .symfony2plugin .Symfony2Icons ;
1210import fr .adrienbrault .idea .symfony2plugin .codeInsight .GotoCompletionProvider ;
1311import fr .adrienbrault .idea .symfony2plugin .codeInsight .GotoCompletionRegistrar ;
1412import fr .adrienbrault .idea .symfony2plugin .codeInsight .GotoCompletionRegistrarParameter ;
1513import fr .adrienbrault .idea .symfony2plugin .codeInsight .utils .GotoCompletionUtil ;
1614import fr .adrienbrault .idea .symfony2plugin .config .component .ParameterLookupElement ;
15+ import fr .adrienbrault .idea .symfony2plugin .config .yaml .ParameterPercentWrapInsertHandler ;
1716import fr .adrienbrault .idea .symfony2plugin .dic .ContainerParameter ;
1817import fr .adrienbrault .idea .symfony2plugin .dic .ServiceCompletionProvider ;
18+ import fr .adrienbrault .idea .symfony2plugin .dic .container .util .DotEnvUtil ;
1919import fr .adrienbrault .idea .symfony2plugin .dic .container .util .ServiceContainerUtil ;
2020import fr .adrienbrault .idea .symfony2plugin .stubs .ContainerCollectionResolver ;
2121import fr .adrienbrault .idea .symfony2plugin .util .MethodMatcher ;
2222import fr .adrienbrault .idea .symfony2plugin .util .PhpElementsUtil ;
2323import fr .adrienbrault .idea .symfony2plugin .util .completion .TagNameCompletionProvider ;
2424import fr .adrienbrault .idea .symfony2plugin .util .dict .ServiceUtil ;
25+ import org .apache .commons .lang3 .StringUtils ;
2526import org .jetbrains .annotations .NotNull ;
2627
2728import java .util .*;
2829import java .util .function .Function ;
30+ import java .util .regex .Matcher ;
31+ import java .util .regex .Pattern ;
2932import java .util .stream .Collectors ;
3033
3134/**
@@ -72,8 +75,8 @@ public void register(@NotNull GotoCompletionRegistrarParameter registrar) {
7275 }
7376 );
7477
75- // #[Autowire('<caret> ')]
76- // #[Autowire(value: '<caret> ')]
78+ // #[Autowire('%kernel.project_dir% ')]
79+ // #[Autowire(value: '%kernel.project_dir% ')]
7780 registrar .register (
7881 PlatformPatterns .or (
7982 PhpElementsUtil .getFirstAttributeStringPattern (ServiceContainerUtil .AUTOWIRE_ATTRIBUTE_CLASS ),
@@ -86,7 +89,7 @@ public void register(@NotNull GotoCompletionRegistrarParameter registrar) {
8689
8790 PhpAttribute phpAttribute = PsiTreeUtil .getParentOfType (context , PhpAttribute .class );
8891 if (phpAttribute != null ) {
89- return new ParameterContributor ((StringLiteralExpression ) context );
92+ return new ParameterWithPercentWrapContributor ((StringLiteralExpression ) context );
9093 }
9194
9295 return null ;
@@ -124,8 +127,49 @@ public void register(@NotNull GotoCompletionRegistrarParameter registrar) {
124127 }
125128 );
126129
130+ // #[Autowire(param: 'kernel.debug')]
131+ registrar .register (
132+ PhpElementsUtil .getAttributeNamedArgumentStringPattern (ServiceContainerUtil .AUTOWIRE_ATTRIBUTE_CLASS , "param" ),
133+ psiElement -> {
134+ PsiElement context = psiElement .getContext ();
135+ if (!(context instanceof StringLiteralExpression )) {
136+ return null ;
137+ }
138+
139+ PhpAttribute phpAttribute = PsiTreeUtil .getParentOfType (context , PhpAttribute .class );
140+ if (phpAttribute != null ) {
141+ return new ParameterContributor ((StringLiteralExpression ) context );
142+ }
143+
144+ return null ;
145+ }
146+ );
147+
148+ // #[Autowire(env: 'SOME_ENV_VAR')]
149+ registrar .register (
150+ PhpElementsUtil .getAttributeNamedArgumentStringPattern (ServiceContainerUtil .AUTOWIRE_ATTRIBUTE_CLASS , "env" ),
151+ psiElement -> {
152+ PsiElement context = psiElement .getContext ();
153+ if (!(context instanceof StringLiteralExpression )) {
154+ return null ;
155+ }
156+
157+ PhpAttribute phpAttribute = PsiTreeUtil .getParentOfType (context , PhpAttribute .class );
158+ if (phpAttribute != null ) {
159+ return new EnvironmentVariableContributor ((StringLiteralExpression ) context );
160+ }
161+
162+ return null ;
163+ }
164+ );
165+
127166 // #[Autowire(service: 'some_service')]
128167 // #[AsDecorator(decorates: 'some_service')]
168+ // #[AutowireServiceClosure('some_service')]
169+ // #[AutowireServiceClosure(service: 'some_service')]
170+ // #[AutowireMethodOf('some_service')]
171+ // #[AutowireMethodOf(service: 'some_service')]
172+ // #[AutowireCallable(service: 'some_service')]
129173 registrar .register (
130174 PlatformPatterns .or (
131175 PhpElementsUtil .getAttributeNamedArgumentStringPattern (ServiceContainerUtil .AUTOWIRE_ATTRIBUTE_CLASS , "service" ),
@@ -145,7 +189,20 @@ public void register(@NotNull GotoCompletionRegistrarParameter registrar) {
145189 // #[AutowireLocator(exclude: ['app.some_tag'])]
146190 // #[AutowireLocator(exclude: 'app.some_tag')]
147191 PhpElementsUtil .getAttributeNamedArgumentArrayStringPattern (ServiceContainerUtil .AUTOWIRE_LOCATOR_ATTRIBUTE_CLASS , "exclude" ),
148- PhpElementsUtil .getAttributeNamedArgumentStringPattern (ServiceContainerUtil .AUTOWIRE_LOCATOR_ATTRIBUTE_CLASS , "exclude" )
192+ PhpElementsUtil .getAttributeNamedArgumentStringPattern (ServiceContainerUtil .AUTOWIRE_LOCATOR_ATTRIBUTE_CLASS , "exclude" ),
193+
194+ // #[AutowireServiceClosure('some_service')]
195+ // #[AutowireServiceClosure(service: 'some_service')]
196+ PhpElementsUtil .getFirstAttributeStringPattern (ServiceContainerUtil .AUTOWIRE_SERVICE_CLOSURE_ATTRIBUTE_CLASS ),
197+ PhpElementsUtil .getAttributeNamedArgumentStringPattern (ServiceContainerUtil .AUTOWIRE_SERVICE_CLOSURE_ATTRIBUTE_CLASS , "service" ),
198+
199+ // #[AutowireMethodOf('some_service')]
200+ // #[AutowireMethodOf(service: 'some_service')]
201+ PhpElementsUtil .getFirstAttributeStringPattern (ServiceContainerUtil .AUTOWIRE_METHOD_OF_ATTRIBUTE_CLASS ),
202+ PhpElementsUtil .getAttributeNamedArgumentStringPattern (ServiceContainerUtil .AUTOWIRE_METHOD_OF_ATTRIBUTE_CLASS , "service" ),
203+
204+ // #[AutowireCallable(service: 'some_service')]
205+ PhpElementsUtil .getAttributeNamedArgumentStringPattern (ServiceContainerUtil .AUTOWIRE_CALLABLE_ATTRIBUTE_CLASS , "service" )
149206 ),
150207 psiElement -> {
151208 PsiElement context = psiElement .getContext ();
@@ -162,6 +219,24 @@ public void register(@NotNull GotoCompletionRegistrarParameter registrar) {
162219 }
163220 );
164221
222+ // #[AutowireCallable(service: 'some_service', method: 'methodName')]
223+ registrar .register (
224+ PhpElementsUtil .getAttributeNamedArgumentStringPattern (ServiceContainerUtil .AUTOWIRE_CALLABLE_ATTRIBUTE_CLASS , "method" ),
225+ psiElement -> {
226+ PsiElement context = psiElement .getContext ();
227+ if (!(context instanceof StringLiteralExpression )) {
228+ return null ;
229+ }
230+
231+ PhpAttribute phpAttribute = PsiTreeUtil .getParentOfType (context , PhpAttribute .class );
232+ if (phpAttribute != null ) {
233+ return new AutowireCallableMethodContributor ((StringLiteralExpression ) context , phpAttribute );
234+ }
235+
236+ return null ;
237+ }
238+ );
239+
165240 // #[When('dev')]
166241 registrar .register (
167242 PlatformPatterns .or (
@@ -255,4 +330,132 @@ public ServiceContributor(@NotNull StringLiteralExpression element) {
255330 return List .of (phpClass );
256331 }
257332 }
333+
334+ private static class EnvironmentVariableContributor extends GotoCompletionProvider {
335+ public EnvironmentVariableContributor (@ NotNull StringLiteralExpression element ) {
336+ super (element );
337+ }
338+
339+ @ Override
340+ public @ NotNull Collection <LookupElement > getLookupElements () {
341+ Collection <LookupElement > results = new ArrayList <>();
342+
343+ for (String envVar : DotEnvUtil .getEnvironmentVariables (getProject ())) {
344+ results .add (LookupElementBuilder .create (envVar ).withIcon (Symfony2Icons .SYMFONY ));
345+ }
346+
347+ return results ;
348+ }
349+
350+ @ Override
351+ public @ NotNull Collection <PsiElement > getPsiTargets (PsiElement element ) {
352+ String contents = GotoCompletionUtil .getStringLiteralValue (element );
353+ if (contents == null ) {
354+ return Collections .emptyList ();
355+ }
356+
357+ // Strip processor prefix if present: bool:SOME_ENV_VAR -> SOME_ENV_VAR
358+ // Supports: int:, bool:, string:, json:, resolve:, etc.
359+ Matcher matcher = Pattern .compile ("^[\\ w-_^:]+:(.*)$" , Pattern .MULTILINE ).matcher (contents );
360+ if (matcher .find ()) {
361+ contents = matcher .group (1 );
362+ }
363+
364+ return DotEnvUtil .getEnvironmentVariableTargets (getProject (), contents );
365+ }
366+ }
367+
368+ private static class ParameterWithPercentWrapContributor extends GotoCompletionProvider {
369+ public ParameterWithPercentWrapContributor (@ NotNull StringLiteralExpression element ) {
370+ super (element );
371+ }
372+
373+ @ Override
374+ public @ NotNull Collection <LookupElement > getLookupElements () {
375+ Collection <LookupElement > results = new ArrayList <>();
376+
377+ for (Map .Entry <String , ContainerParameter > entry : ContainerCollectionResolver .getParameters (getProject ()).entrySet ()) {
378+ results .add (new ParameterLookupElement (
379+ entry .getValue (),
380+ ParameterPercentWrapInsertHandler .getInstance (),
381+ getElement ().getText ()
382+ ));
383+ }
384+
385+ return results ;
386+ }
387+
388+ @ Override
389+ public @ NotNull Collection <PsiElement > getPsiTargets (PsiElement element ) {
390+ String contents = GotoCompletionUtil .getStringLiteralValue (element );
391+ if (contents == null ) {
392+ return Collections .emptyList ();
393+ }
394+
395+ // Strip % characters if present: %kernel.debug% -> kernel.debug
396+ contents = StringUtils .strip (contents , "%" );
397+
398+ return ServiceUtil .getParameterDefinition (getProject (), contents );
399+ }
400+ }
401+
402+ private static class AutowireCallableMethodContributor extends GotoCompletionProvider {
403+ private final PhpAttribute phpAttribute ;
404+
405+ public AutowireCallableMethodContributor (@ NotNull StringLiteralExpression element , @ NotNull PhpAttribute phpAttribute ) {
406+ super (element );
407+ this .phpAttribute = phpAttribute ;
408+ }
409+
410+ @ Override
411+ public @ NotNull Collection <LookupElement > getLookupElements () {
412+ Collection <LookupElement > results = new ArrayList <>();
413+
414+ PhpClass phpClass = getServiceClass ();
415+ if (phpClass == null ) {
416+ return results ;
417+ }
418+
419+ for (Method method : phpClass .getMethods ()) {
420+ // Only public non-static methods
421+ if (method .getAccess ().isPublic () && !method .isStatic ()) {
422+ results .add (LookupElementBuilder .create (method .getName ())
423+ .withIcon (method .getIcon ())
424+ .withTypeText (phpClass .getName (), true ));
425+ }
426+ }
427+
428+ return results ;
429+ }
430+
431+ @ Override
432+ public @ NotNull Collection <PsiElement > getPsiTargets (PsiElement element ) {
433+ String methodName = GotoCompletionUtil .getStringLiteralValue (element );
434+ if (methodName == null ) {
435+ return Collections .emptyList ();
436+ }
437+
438+ PhpClass phpClass = getServiceClass ();
439+ if (phpClass == null ) {
440+ return Collections .emptyList ();
441+ }
442+
443+ Method method = phpClass .findMethodByName (methodName );
444+ if (method != null && method .getAccess ().isPublic () && !method .isStatic ()) {
445+ return List .of (method );
446+ }
447+
448+ return Collections .emptyList ();
449+ }
450+
451+ private PhpClass getServiceClass () {
452+ // Find the 'service' argument in the same attribute using the utility method
453+ String serviceId = PhpElementsUtil .getAttributeArgumentStringByName (phpAttribute , "service" );
454+ if (serviceId != null && !serviceId .isEmpty ()) {
455+ return ServiceUtil .getResolvedClassDefinition (getProject (), serviceId );
456+ }
457+
458+ return null ;
459+ }
460+ }
258461}
0 commit comments