Skip to content

Commit 6c3b6b5

Browse files
authored
Merge pull request #1978 from Haehnchen/feature/1969-named-arguments-validator-constraint
#1969 Support translation autocomplete on form constraints with named arguments
2 parents d1452ff + 53904db commit 6c3b6b5

File tree

4 files changed

+176
-3
lines changed

4 files changed

+176
-3
lines changed

src/main/java/fr/adrienbrault/idea/symfony2plugin/translation/ValidatorTranslationGotoCompletionRegistrar.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,26 @@ public class ValidatorTranslationGotoCompletionRegistrar implements GotoCompleti
2121
"Symfony\\Component\\Validator\\Constraint"
2222
);
2323

24+
private static final PhpPsiMatcher.NamedValueWithKeyAndNewExpression.Matcher CONSTRAINT_MESSAGE_NAMED = new PhpPsiMatcher.NamedValueWithKeyAndNewExpression.Matcher(
25+
"message",
26+
"Symfony\\Component\\Validator\\Constraint"
27+
);
28+
2429
@Override
2530
public void register(@NotNull GotoCompletionRegistrarParameter registrar) {
2631
// new Constraint(['message' => '<caret>'])
32+
// new Constraint(message: '<caret>')
2733
registrar.register(
28-
PhpPsiMatcher.ArrayValueWithKeyAndNewExpression.pattern(), psiElement -> {
34+
PlatformPatterns.or(
35+
PhpPsiMatcher.ArrayValueWithKeyAndNewExpression.pattern(),
36+
PhpPsiMatcher.NamedValueWithKeyAndNewExpression.pattern()
37+
), psiElement -> {
2938
PsiElement parent = psiElement.getParent();
3039
if (!(parent instanceof StringLiteralExpression)) {
3140
return null;
3241
}
3342

34-
PhpPsiMatcher.ArrayValueWithKeyAndNewExpression.Result result = PhpPsiMatcher.match(parent, CONSTRAINT_MESSAGE);
35-
if (result == null) {
43+
if (PhpPsiMatcher.match(parent, CONSTRAINT_MESSAGE) == null && PhpPsiMatcher.match(parent, CONSTRAINT_MESSAGE_NAMED) == null) {
3644
return null;
3745
}
3846

src/main/java/fr/adrienbrault/idea/symfony2plugin/util/psi/PhpPsiMatcher.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.intellij.psi.PsiElement;
44
import fr.adrienbrault.idea.symfony2plugin.util.psi.matcher.ArrayValueWithKeyAndMethodMatcher;
55
import fr.adrienbrault.idea.symfony2plugin.util.psi.matcher.ArrayValueWithKeyAndNewExpressionMatcher;
6+
import fr.adrienbrault.idea.symfony2plugin.util.psi.matcher.NamedValueWithKeyAndNewExpressionMatcher;
67
import org.jetbrains.annotations.NotNull;
78
import org.jetbrains.annotations.Nullable;
89

@@ -24,6 +25,15 @@ public static ArrayValueWithKeyAndNewExpressionMatcher.Result match(@NotNull Psi
2425
return ArrayValueWithKeyAndNewExpression.match(psiElement, matcher);
2526
}
2627

28+
/**
29+
* new Foo(message: '<caret>')
30+
*/
31+
@Nullable
32+
public static NamedValueWithKeyAndNewExpression.Result match(@NotNull PsiElement psiElement, @NotNull NamedValueWithKeyAndNewExpression.Matcher matcher) {
33+
return NamedValueWithKeyAndNewExpression.match(psiElement, matcher);
34+
}
35+
2736
final public static class ArrayValueWithKeyAndMethod extends ArrayValueWithKeyAndMethodMatcher {}
2837
final public static class ArrayValueWithKeyAndNewExpression extends ArrayValueWithKeyAndNewExpressionMatcher {}
38+
final public static class NamedValueWithKeyAndNewExpression extends NamedValueWithKeyAndNewExpressionMatcher {}
2939
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package fr.adrienbrault.idea.symfony2plugin.util.psi.matcher;
2+
3+
import com.intellij.patterns.PlatformPatterns;
4+
import com.intellij.patterns.PsiElementPattern;
5+
import com.intellij.psi.PsiElement;
6+
import com.intellij.psi.PsiWhiteSpace;
7+
import com.intellij.psi.util.PsiTreeUtil;
8+
import com.jetbrains.php.lang.lexer.PhpTokenTypes;
9+
import com.jetbrains.php.lang.patterns.PhpPatterns;
10+
import com.jetbrains.php.lang.psi.elements.NewExpression;
11+
import com.jetbrains.php.lang.psi.elements.ParameterList;
12+
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
13+
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
14+
import org.jetbrains.annotations.NotNull;
15+
import org.jetbrains.annotations.Nullable;
16+
17+
/**
18+
* @author Daniel Espendiller <daniel@espendiller.net>
19+
*/
20+
public class NamedValueWithKeyAndNewExpressionMatcher {
21+
22+
/**
23+
* new Foobar(
24+
* route: '<caret>',
25+
* );
26+
*/
27+
@NotNull
28+
public static PsiElementPattern.Capture<PsiElement> pattern() {
29+
return PhpPatterns.psiElement().withParent(
30+
PlatformPatterns.psiElement(StringLiteralExpression.class).afterLeafSkipping(PlatformPatterns.or(
31+
PlatformPatterns.psiElement(PsiWhiteSpace.class),
32+
PlatformPatterns.psiElement(PhpTokenTypes.WHITE_SPACE),
33+
PlatformPatterns.psiElement(PhpTokenTypes.opCOLON)
34+
), PlatformPatterns.psiElement(PhpTokenTypes.IDENTIFIER)).withParent(PlatformPatterns.psiElement(ParameterList.class))
35+
);
36+
}
37+
38+
/**
39+
* new Foobar(
40+
* route: '<caret>',
41+
* );
42+
*/
43+
@Nullable
44+
public static Result match(@NotNull PsiElement psiElement, @NotNull Matcher matcher) {
45+
PsiElement parameterList = psiElement.getParent();
46+
if (parameterList instanceof ParameterList) {
47+
PsiElement newExpression = parameterList.getParent();
48+
if (newExpression instanceof NewExpression) {
49+
for (NewExpressionCall newExpressionCall : matcher.getNewExpressionCalls()) {
50+
PsiElement colon = PsiTreeUtil.prevCodeLeaf(psiElement);
51+
if (colon == null || colon.getNode().getElementType() != PhpTokenTypes.opCOLON) {
52+
continue;
53+
}
54+
55+
PsiElement identifier = PsiTreeUtil.prevCodeLeaf(colon);
56+
if (identifier == null || identifier.getNode().getElementType() != PhpTokenTypes.IDENTIFIER || !newExpressionCall.getNamedArgument().equals(identifier.getText())) {
57+
continue;
58+
}
59+
60+
if (!PhpElementsUtil.isNewExpressionPhpClassWithInstance((NewExpression) newExpression, newExpressionCall.getClazz())) {
61+
continue;
62+
}
63+
64+
return new Result(newExpressionCall, (NewExpression) newExpression, identifier);
65+
}
66+
}
67+
}
68+
69+
return null;
70+
}
71+
72+
public static class Matcher {
73+
@NotNull
74+
private final NewExpressionCall[] classes;
75+
76+
public Matcher(@NotNull String namedArgument, @NotNull String clazz) {
77+
this.classes = new NewExpressionCall[] { new NewExpressionCall(clazz, namedArgument)};
78+
}
79+
80+
public Matcher(@NotNull NewExpressionCall... classes) {
81+
this.classes = classes;
82+
}
83+
84+
@NotNull
85+
public NewExpressionCall[] getNewExpressionCalls() {
86+
return classes;
87+
}
88+
}
89+
90+
public static class Result {
91+
@NotNull
92+
private final NewExpression newExpression;
93+
94+
@NotNull
95+
private final NewExpressionCall expressionCall;
96+
97+
@NotNull
98+
private final PsiElement arrayKey;
99+
100+
public Result(@NotNull NewExpressionCall expressionCall, @NotNull NewExpression newExpression, @NotNull PsiElement arrayKey) {
101+
this.expressionCall = expressionCall;
102+
this.newExpression = newExpression;
103+
this.arrayKey = arrayKey;
104+
}
105+
106+
@NotNull
107+
public NewExpression getNewExpression() {
108+
return newExpression;
109+
}
110+
111+
@NotNull
112+
public NewExpressionCall getExpressionCall() {
113+
return expressionCall;
114+
}
115+
116+
@NotNull
117+
public PsiElement getArrayKey() {
118+
return arrayKey;
119+
}
120+
}
121+
122+
public static class NewExpressionCall {
123+
@NotNull
124+
private final String clazz;
125+
126+
@NotNull
127+
private final String namedArgument;
128+
129+
public NewExpressionCall(@NotNull String clazz, @NotNull String namedArgument) {
130+
this.clazz = clazz;
131+
this.namedArgument = namedArgument;
132+
}
133+
134+
@NotNull
135+
public String getClazz() {
136+
return clazz;
137+
}
138+
139+
@NotNull
140+
public String getNamedArgument() {
141+
return namedArgument;
142+
}
143+
}
144+
}

src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/translation/ValidatorTranslationGotoCompletionRegistrarTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ public void testThatMessageValueForConstraintProvideValidatorTranslations() {
2929
);
3030
}
3131

32+
public void testThatMessageValueForConstraintForNamedMessageIsTranslationed() {
33+
assertCompletionContains(PhpFileType.INSTANCE, "<?php\n" +
34+
"$f = new MyConstraintMessage(message: '<caret>')",
35+
"foo_yaml.symfony.great"
36+
);
37+
38+
assertNavigationMatch(PhpFileType.INSTANCE, "<?php\n" +
39+
"$f = new MyConstraintMessage(message: 'foo_yaml.symfony<caret>.great')"
40+
);
41+
}
42+
3243
public void testThatExecutionContextProvidesTranslation() {
3344
assertCompletionContains(PhpFileType.INSTANCE, "<?php\n" +
3445
"/** @var $f \\Symfony\\Component\\Validator\\Context\\ExecutionContextInterface */\n" +

0 commit comments

Comments
 (0)