Skip to content

Commit 33d4c93

Browse files
committed
Add language injection for method parameters (CSS, XPath, DQL, JSON)
1 parent 3e87802 commit 33d4c93

File tree

2 files changed

+150
-0
lines changed

2 files changed

+150
-0
lines changed
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
package fr.adrienbrault.idea.symfony2plugin.lang;
2+
3+
import com.intellij.lang.Language;
4+
import com.intellij.lang.injection.MultiHostInjector;
5+
import com.intellij.lang.injection.MultiHostRegistrar;
6+
import com.intellij.openapi.util.TextRange;
7+
import com.intellij.psi.PsiElement;
8+
import com.intellij.psi.PsiLanguageInjectionHost;
9+
import com.jetbrains.php.lang.psi.elements.*;
10+
import com.jetbrains.php.lang.psi.elements.impl.StringLiteralExpressionImpl;
11+
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
12+
import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher;
13+
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
14+
import org.jetbrains.annotations.NotNull;
15+
import org.mozilla.javascript.ast.VariableDeclaration;
16+
17+
import java.util.Collections;
18+
import java.util.List;
19+
20+
public class ParameterLanguageInjector implements MultiHostInjector {
21+
22+
private static final MethodMatcher.CallToSignature[] CSS_SELECTOR_SIGNATURES = {
23+
new MethodMatcher.CallToSignature("\\Symfony\\Component\\DomCrawler\\Crawler", "filter"),
24+
new MethodMatcher.CallToSignature("\\Symfony\\Component\\DomCrawler\\Crawler", "children"),
25+
new MethodMatcher.CallToSignature("\\Symfony\\Component\\CssSelector\\CssSelectorConverter", "toXPath"),
26+
};
27+
28+
private static final MethodMatcher.CallToSignature[] XPATH_SIGNATURES = {
29+
new MethodMatcher.CallToSignature("\\Symfony\\Component\\DomCrawler\\Crawler", "filterXPath"),
30+
new MethodMatcher.CallToSignature("\\Symfony\\Component\\DomCrawler\\Crawler", "evaluate"),
31+
};
32+
33+
private static final MethodMatcher.CallToSignature[] JSON_SIGNATURES = {
34+
//new MethodMatcher.CallToSignature("\\Symfony\\Component\\HttpFoundation\\JsonResponse", "__construct"),
35+
new MethodMatcher.CallToSignature("\\Symfony\\Component\\HttpFoundation\\JsonResponse", "fromJsonString"),
36+
new MethodMatcher.CallToSignature("\\Symfony\\Component\\HttpFoundation\\JsonResponse", "setJson"),
37+
};
38+
39+
private static final MethodMatcher.CallToSignature[] DQL_SIGNATURES = {
40+
new MethodMatcher.CallToSignature("\\Doctrine\\ORM\\EntityManager", "createQuery"),
41+
new MethodMatcher.CallToSignature("\\Doctrine\\ORM\\Query", "setDQL"),
42+
};
43+
44+
private final MethodLanguageInjection[] LANGUAGE_INJECTIONS = {
45+
new MethodLanguageInjection("CSS", "@media all { ", " }", CSS_SELECTOR_SIGNATURES),
46+
new MethodLanguageInjection("XPath", null, null, XPATH_SIGNATURES),
47+
new MethodLanguageInjection("JSON", null, null, JSON_SIGNATURES),
48+
new MethodLanguageInjection("DQL", null, null, DQL_SIGNATURES),
49+
};
50+
51+
public ParameterLanguageInjector() {
52+
}
53+
54+
@NotNull
55+
@Override
56+
public List<? extends Class<? extends PsiElement>> elementsToInjectIn() {
57+
return Collections.singletonList(StringLiteralExpressionImpl.class);
58+
}
59+
60+
@Override
61+
public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement element) {
62+
if (!(element instanceof StringLiteralExpression) || !((PsiLanguageInjectionHost) element).isValidHost()) {
63+
return;
64+
}
65+
if (!Symfony2ProjectComponent.isEnabled(element.getProject())) {
66+
return;
67+
}
68+
69+
final StringLiteralExpressionImpl expr = (StringLiteralExpressionImpl) element;
70+
71+
PsiElement parent = expr.getParent();
72+
73+
if (!(parent instanceof ParameterList) || expr.getPrevPsiSibling() != null) {
74+
return;
75+
}
76+
77+
parent = parent.getParent();
78+
79+
for (MethodLanguageInjection languageInjection : LANGUAGE_INJECTIONS) {
80+
Language language = languageInjection.getLanguage();
81+
if (language == null) {
82+
continue;
83+
}
84+
// $crawler->filter('...')
85+
// $em->createQuery('...')
86+
// JsonResponse::fromJsonString('...')
87+
if (parent instanceof MethodReference) {
88+
if (PhpElementsUtil.isMethodReferenceInstanceOf((MethodReference) parent, languageInjection.getSignatures())) {
89+
injectLanguage(registrar, expr, language, languageInjection);
90+
return;
91+
}
92+
}
93+
// $dql = "...";
94+
// else if (parent instanceof AssignmentExpression) {
95+
// if ("DQL".equals(language.getID())) {
96+
// PhpPsiElement variable = ((AssignmentExpression) parent).getVariable();
97+
//
98+
// if (variable instanceof VariableDeclaration) {
99+
// if ("dql".equals(variable.getName())) {
100+
// injectLanguage(registrar, expr, language, languageInjection);
101+
// return;
102+
// }
103+
// }
104+
// }
105+
// }
106+
}
107+
}
108+
109+
private void injectLanguage(@NotNull MultiHostRegistrar registrar, @NotNull StringLiteralExpressionImpl element, Language language, MethodLanguageInjection languageInjection) {
110+
final int length = ((StringLiteralExpression) element).getContents().length();
111+
final TextRange range = TextRange.create(1, length + 1);
112+
113+
registrar.startInjecting(language)
114+
.addPlace(languageInjection.getPrefix(), languageInjection.getSuffix(), element, range)
115+
.doneInjecting();
116+
}
117+
118+
private class MethodLanguageInjection {
119+
private final Language language;
120+
private final String prefix;
121+
private final String suffix;
122+
private final MethodMatcher.CallToSignature[] signatures;
123+
124+
MethodLanguageInjection(@NotNull String languageId, String prefix, String suffix, MethodMatcher.CallToSignature[] signatures) {
125+
126+
this.language = Language.findLanguageByID(languageId);
127+
this.prefix = prefix;
128+
this.suffix = suffix;
129+
this.signatures = signatures;
130+
}
131+
132+
public Language getLanguage() {
133+
return language;
134+
}
135+
136+
public String getPrefix() {
137+
return prefix;
138+
}
139+
140+
public String getSuffix() {
141+
return suffix;
142+
}
143+
144+
public MethodMatcher.CallToSignature[] getSignatures() {
145+
return signatures;
146+
}
147+
}
148+
}

src/main/resources/META-INF/plugin.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@
254254
<codeInsight.parameterNameHints language="XML" implementationClass="fr.adrienbrault.idea.symfony2plugin.dic.ServiceArgumentParameterHintsProvider"/>
255255
<codeInsight.parameterNameHints language="yaml" implementationClass="fr.adrienbrault.idea.symfony2plugin.dic.ServiceArgumentParameterHintsProvider"/>
256256

257+
<multiHostInjector implementation="fr.adrienbrault.idea.symfony2plugin.lang.ParameterLanguageInjector"/>
258+
257259
<localInspection groupPath="Symfony" shortName="PhpRouteMissingInspection" displayName="Route Missing"
258260
groupName="Route"
259261
enabledByDefault="true" level="WARNING"

0 commit comments

Comments
 (0)