44import com .intellij .patterns .PatternCondition ;
55import com .intellij .patterns .PlatformPatterns ;
66import com .intellij .patterns .PsiElementPattern ;
7+ import com .intellij .patterns .StandardPatterns ;
78import com .intellij .psi .PsiElement ;
89import com .intellij .util .ProcessingContext ;
9- import com .jetbrains .php .lang .documentation .phpdoc .lexer .PhpDocTokenTypes ;
10- import com .jetbrains .php .lang .documentation .phpdoc .parser .PhpDocElementTypes ;
1110import com .jetbrains .php .lang .documentation .phpdoc .psi .tags .PhpDocTag ;
1211import com .jetbrains .php .lang .lexer .PhpTokenTypes ;
1312import com .jetbrains .php .lang .psi .elements .ParameterList ;
1817import fr .adrienbrault .idea .symfony2plugin .codeInsight .GotoCompletionProviderLookupArguments ;
1918import fr .adrienbrault .idea .symfony2plugin .codeInsight .GotoCompletionRegistrar ;
2019import fr .adrienbrault .idea .symfony2plugin .codeInsight .GotoCompletionRegistrarParameter ;
20+ import fr .adrienbrault .idea .symfony2plugin .expressionLanguage .psi .ExpressionLanguageCallExpr ;
21+ import fr .adrienbrault .idea .symfony2plugin .expressionLanguage .psi .ExpressionLanguageLiteralExpr ;
22+ import fr .adrienbrault .idea .symfony2plugin .expressionLanguage .psi .ExpressionLanguageRefExpr ;
23+ import fr .adrienbrault .idea .symfony2plugin .expressionLanguage .psi .ExpressionLanguageStringLiteral ;
2124import fr .adrienbrault .idea .symfony2plugin .security .utils .VoterUtil ;
2225import fr .adrienbrault .idea .symfony2plugin .util .PhpElementsUtil ;
23- import org .apache .commons .lang .StringUtils ;
2426import org .jetbrains .annotations .NotNull ;
2527
2628import java .util .Collection ;
2729import java .util .Collections ;
2830import java .util .HashSet ;
29- import java .util .regex .Matcher ;
30- import java .util .regex .Pattern ;
3131
3232/**
3333 * @author Daniel Espendiller <daniel@espendiller.net>
@@ -40,39 +40,24 @@ public class AnnotationExpressionGotoCompletionRegistrar implements GotoCompleti
4040 public void register (@ NotNull GotoCompletionRegistrarParameter registrar ) {
4141 // "@Security("is_granted('POST_SHOW', post) and has_role('ROLE_ADMIN')")"
4242 registrar .register (
43- PlatformPatterns .or (getDocTagStringPattern (), getAttributeStringPattern ()),
43+ PlatformPatterns .psiElement ()
44+ .withParent (PlatformPatterns
45+ .psiElement (ExpressionLanguageStringLiteral .class )
46+ .withParent (PlatformPatterns
47+ .psiElement (ExpressionLanguageLiteralExpr .class )
48+ .withParent (PlatformPatterns
49+ .psiElement (ExpressionLanguageCallExpr .class )
50+ .withFirstChild (PlatformPatterns
51+ .psiElement (ExpressionLanguageRefExpr .class )
52+ .withText (StandardPatterns .string ().oneOf ("has_role" , "is_granted" ))
53+ )
54+ )
55+ )
56+ ),
4457 MyGotoCompletionProvider ::new
4558 );
4659 }
4760
48- @ NotNull
49- private PsiElementPattern .Capture <PsiElement > getAttributeStringPattern () {
50- // #[Security("is_granted('POST_SHOW')")]
51- return PlatformPatterns .psiElement ().withElementType (PlatformPatterns .elementType ().or (
52- PhpTokenTypes .STRING_LITERAL_SINGLE_QUOTE ,
53- PhpTokenTypes .STRING_LITERAL
54- ))
55- .withParent (PlatformPatterns .psiElement (StringLiteralExpression .class )
56- .withParent (PlatformPatterns .psiElement (ParameterList .class )
57- .withParent (PlatformPatterns .psiElement (PhpAttribute .class )
58- .with (PhpDocInstancePatternCondition .INSTANCE )
59- )
60- )
61- );
62- }
63-
64- @ NotNull
65- private PsiElementPattern .Capture <PsiElement > getDocTagStringPattern () {
66- return PlatformPatterns .psiElement (PhpDocTokenTypes .DOC_STRING )
67- .withParent (PlatformPatterns .psiElement (StringLiteralExpression .class )
68- .withParent (PlatformPatterns .psiElement (PhpDocElementTypes .phpDocAttributeList )
69- .withParent (PlatformPatterns .psiElement (PhpDocTag .class )
70- .with (PhpDocInstancePatternCondition .INSTANCE )
71- )
72- )
73- );
74- }
75-
7661 /**
7762 * "@Security("has_role('ROLE_FOOBAR')")"
7863 * "@Security("is_granted('POST_SHOW', post) and has_role('ROLE_ADMIN')")"
@@ -87,16 +72,7 @@ public void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arg
8772 final CompletionResultSet resultSet = arguments .getResultSet ();
8873 String blockNamePrefix = resultSet .getPrefixMatcher ().getPrefix ();
8974
90- // find caret position:
91- // - "has_role('"
92- // - "has_role('YAML_ROLE_"
93- if (!blockNamePrefix .matches ("^.*(has_role|is_granted)\\ s*\\ (\\ s*['|\" ][\\ w-]*$" )) {
94- return ;
95- }
96-
97- // clear prefix caret string; for a clean completion independent from inside content
98- String substring = blockNamePrefix .replaceAll ("^(.*(has_role|is_granted)\\ s*\\ (\\ s*['|\" ])" , "" );
99- CompletionResultSet myResultSet = resultSet .withPrefixMatcher (substring );
75+ CompletionResultSet myResultSet = resultSet .withPrefixMatcher (blockNamePrefix );
10076
10177 VoterUtil .LookupElementPairConsumer consumer = new VoterUtil .LookupElementPairConsumer ();
10278 VoterUtil .visitAttribute (getProject (), consumer );
@@ -106,70 +82,22 @@ public void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arg
10682 @ NotNull
10783 @ Override
10884 public Collection <PsiElement > getPsiTargets (PsiElement element ) {
109- String contents = null ;
110- if (getElement ().getNode ().getElementType () == PhpDocTokenTypes .DOC_STRING ) {
111- // @Security
112- PsiElement parent = getElement ().getParent ();
113- if (!(parent instanceof StringLiteralExpression )) {
114- return Collections .emptyList ();
115- }
116-
117- contents = ((StringLiteralExpression ) parent ).getContents ();
118- } else {
119- // @Security
120- PsiElement parent = getElement ().getParent ();
121- if (parent instanceof StringLiteralExpression ) {
122- contents = ((StringLiteralExpression ) parent ).getContents ();
123- }
124- }
125-
126- if (StringUtils .isBlank (contents )) {
85+ var text = getElement ().getText ();
86+ if (text .length () < 2 ) {
12787 return Collections .emptyList ();
12888 }
12989
130- Collection <String > roles = new HashSet <>();
131- for (String regex : new String []{"is_granted\\ s*\\ (\\ s*['|\" ]([^'\" ]+)['|\" ]\\ s*[\\ )|,]" , "has_role\\ s*\\ (\\ s*['|\" ]([^'\" ]+)['|\" ]\\ s*\\ )" }) {
132- Matcher matcher = Pattern .compile (regex ).matcher (contents );
133- while (matcher .find ()){
134- roles .add (matcher .group (1 ));
135- }
136- }
137-
138- if (roles .size () == 0 ) {
139- return Collections .emptyList ();
140- }
141-
142- Collection <PsiElement > targets = new HashSet <>();
90+ // Strip quotes
91+ var role = text .substring (1 , text .length () - 1 );
14392
93+ var targets = new HashSet <PsiElement >();
14494 VoterUtil .visitAttribute (getProject (), pair -> {
145- if (roles . contains ( pair .getFirst ())) {
95+ if (pair .getFirst (). equals ( role )) {
14696 targets .add (pair .getSecond ());
14797 }
14898 });
14999
150100 return targets ;
151101 }
152102 }
153-
154- /**
155- * Check if given PhpDocTag is instance of given Annotation class
156- */
157- private static class PhpDocInstancePatternCondition extends PatternCondition <PsiElement > {
158- private static final PhpDocInstancePatternCondition INSTANCE = new PhpDocInstancePatternCondition ();
159-
160- PhpDocInstancePatternCondition () {
161- super ("PhpDoc/Attribute Instance" );
162- }
163-
164- @ Override
165- public boolean accepts (@ NotNull PsiElement psiElement , ProcessingContext processingContext ) {
166- if (psiElement instanceof PhpDocTag ) {
167- return PhpElementsUtil .isEqualClassName (AnnotationUtil .getAnnotationReference ((PhpDocTag ) psiElement ), SECURITY_ANNOTATION );
168- } else if (psiElement instanceof PhpAttribute ) {
169- return SECURITY_ANNOTATION .equals (((PhpAttribute ) psiElement ).getFQN ());
170- }
171-
172- return false ;
173- }
174- }
175103}
0 commit comments