3
3
import com .intellij .codeInsight .completion .CompletionResultSet ;
4
4
import com .intellij .patterns .PatternCondition ;
5
5
import com .intellij .patterns .PlatformPatterns ;
6
+ import com .intellij .patterns .PsiElementPattern ;
6
7
import com .intellij .psi .PsiElement ;
7
8
import com .intellij .util .ProcessingContext ;
8
9
import com .jetbrains .php .lang .documentation .phpdoc .lexer .PhpDocTokenTypes ;
9
10
import com .jetbrains .php .lang .documentation .phpdoc .parser .PhpDocElementTypes ;
10
11
import com .jetbrains .php .lang .documentation .phpdoc .psi .tags .PhpDocTag ;
12
+ import com .jetbrains .php .lang .lexer .PhpTokenTypes ;
13
+ import com .jetbrains .php .lang .psi .elements .ParameterList ;
14
+ import com .jetbrains .php .lang .psi .elements .PhpAttribute ;
11
15
import com .jetbrains .php .lang .psi .elements .StringLiteralExpression ;
12
16
import de .espend .idea .php .annotation .util .AnnotationUtil ;
13
17
import fr .adrienbrault .idea .symfony2plugin .codeInsight .GotoCompletionProvider ;
16
20
import fr .adrienbrault .idea .symfony2plugin .codeInsight .GotoCompletionRegistrarParameter ;
17
21
import fr .adrienbrault .idea .symfony2plugin .security .utils .VoterUtil ;
18
22
import fr .adrienbrault .idea .symfony2plugin .util .PhpElementsUtil ;
23
+ import org .apache .commons .lang .StringUtils ;
19
24
import org .jetbrains .annotations .NotNull ;
20
25
21
26
import java .util .Collection ;
29
34
*/
30
35
public class AnnotationExpressionGotoCompletionRegistrar implements GotoCompletionRegistrar {
31
36
32
- private static final String SECURITY_ANNOTATION = "Sensio\\ Bundle\\ FrameworkExtraBundle\\ Configuration\\ Security" ;
37
+ private static final String SECURITY_ANNOTATION = "\\ Sensio\\ Bundle\\ FrameworkExtraBundle\\ Configuration\\ Security" ;
33
38
34
39
@ Override
35
40
public void register (@ NotNull GotoCompletionRegistrarParameter registrar ) {
36
41
// "@Security("is_granted('POST_SHOW', post) and has_role('ROLE_ADMIN')")"
37
42
registrar .register (
38
- PlatformPatterns .psiElement (PhpDocTokenTypes .DOC_STRING )
43
+ PlatformPatterns .or (getDocTagStringPattern (), getAttributeStringPattern ()),
44
+ MyGotoCompletionProvider ::new
45
+ );
46
+ }
47
+
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 )
39
67
.withParent (PlatformPatterns .psiElement (StringLiteralExpression .class )
40
68
.withParent (PlatformPatterns .psiElement (PhpDocElementTypes .phpDocAttributeList )
41
69
.withParent (PlatformPatterns .psiElement (PhpDocTag .class )
42
70
.with (PhpDocInstancePatternCondition .INSTANCE )
43
71
)
44
72
)
45
- ),
46
- MyGotoCompletionProvider ::new
47
- );
73
+ );
48
74
}
49
75
50
76
/**
@@ -64,12 +90,13 @@ public void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arg
64
90
// find caret position:
65
91
// - "has_role('"
66
92
// - "has_role('YAML_ROLE_"
67
- if (!blockNamePrefix .matches ("^.*(has_role|is_granted)\\ s*\\ (\\ s*' [\\ w-]*$" )) {
93
+ if (!blockNamePrefix .matches ("^.*(has_role|is_granted)\\ s*\\ (\\ s*['| \" ] [\\ w-]*$" )) {
68
94
return ;
69
95
}
70
96
71
97
// clear prefix caret string; for a clean completion independent from inside content
72
- CompletionResultSet myResultSet = resultSet .withPrefixMatcher ("" );
98
+ String substring = blockNamePrefix .replaceAll ("^(.*(has_role|is_granted)\\ s*\\ (\\ s*['|\" ])" , "" );
99
+ CompletionResultSet myResultSet = resultSet .withPrefixMatcher (substring );
73
100
74
101
VoterUtil .LookupElementPairConsumer consumer = new VoterUtil .LookupElementPairConsumer ();
75
102
VoterUtil .visitAttribute (getProject (), consumer );
@@ -79,17 +106,27 @@ public void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arg
79
106
@ NotNull
80
107
@ Override
81
108
public Collection <PsiElement > getPsiTargets (PsiElement element ) {
82
- if (getElement ().getNode ().getElementType () != PhpDocTokenTypes .DOC_STRING ) {
83
- return Collections .emptyList ();
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
+ }
84
124
}
85
125
86
- PsiElement parent = getElement ().getParent ();
87
- if (!(parent instanceof StringLiteralExpression )) {
126
+ if (StringUtils .isBlank (contents )) {
88
127
return Collections .emptyList ();
89
128
}
90
129
91
- String contents = ((StringLiteralExpression ) parent ).getContents ();
92
-
93
130
Collection <String > roles = new HashSet <>();
94
131
for (String regex : new String []{"is_granted\\ s*\\ (\\ s*['|\" ]([^'\" ]+)['|\" ]\\ s*[\\ )|,]" , "has_role\\ s*\\ (\\ s*['|\" ]([^'\" ]+)['|\" ]\\ s*\\ )" }) {
95
132
Matcher matcher = Pattern .compile (regex ).matcher (contents );
@@ -118,16 +155,21 @@ public Collection<PsiElement> getPsiTargets(PsiElement element) {
118
155
* Check if given PhpDocTag is instance of given Annotation class
119
156
*/
120
157
private static class PhpDocInstancePatternCondition extends PatternCondition <PsiElement > {
121
- private static PhpDocInstancePatternCondition INSTANCE = new PhpDocInstancePatternCondition ();
158
+ private static final PhpDocInstancePatternCondition INSTANCE = new PhpDocInstancePatternCondition ();
122
159
123
160
PhpDocInstancePatternCondition () {
124
- super ("PhpDoc Annotation Instance" );
161
+ super ("PhpDoc/Attribute Instance" );
125
162
}
126
163
127
164
@ Override
128
165
public boolean accepts (@ NotNull PsiElement psiElement , ProcessingContext processingContext ) {
129
- return psiElement instanceof PhpDocTag
130
- && PhpElementsUtil .isEqualClassName (AnnotationUtil .getAnnotationReference ((PhpDocTag ) psiElement ), SECURITY_ANNOTATION );
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 ;
131
173
}
132
174
}
133
175
}
0 commit comments