Skip to content

Commit 6a804f1

Browse files
authored
Merge pull request #1414 from Haehnchen/feature/token-deprecated
provide deprecated inspection for Twig token tags on PhpClass
2 parents c71f999 + e3cf04d commit 6a804f1

File tree

6 files changed

+156
-2
lines changed

6 files changed

+156
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package fr.adrienbrault.idea.symfony2plugin.templating.inspection;
2+
3+
import com.intellij.codeInspection.LocalInspectionTool;
4+
import com.intellij.codeInspection.ProblemHighlightType;
5+
import com.intellij.codeInspection.ProblemsHolder;
6+
import com.intellij.psi.PsiElement;
7+
import com.intellij.psi.PsiElementVisitor;
8+
import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment;
9+
import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag;
10+
import com.jetbrains.php.lang.psi.elements.PhpClass;
11+
import com.jetbrains.twig.TwigTokenTypes;
12+
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
13+
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
14+
import org.apache.commons.lang.StringUtils;
15+
import org.jetbrains.annotations.NotNull;
16+
import org.jetbrains.annotations.Nullable;
17+
18+
/**
19+
* {% ta<caret>g %}
20+
*
21+
* @author Daniel Espendiller <[email protected]>
22+
*/
23+
public class TwigExtensionDeprecatedInspection extends LocalInspectionTool {
24+
25+
@NotNull
26+
public PsiElementVisitor buildVisitor(final @NotNull ProblemsHolder holder, boolean isOnTheFly) {
27+
if (!Symfony2ProjectComponent.isEnabled(holder.getProject())) {
28+
return super.buildVisitor(holder, isOnTheFly);
29+
}
30+
31+
return new PsiElementVisitor() {
32+
@Override
33+
public void visitElement(PsiElement element) {
34+
// {% tag %}
35+
if (element.getNode().getElementType() == TwigTokenTypes.TAG_NAME) {
36+
visitTagTokenName(element);
37+
}
38+
39+
super.visitElement(element);
40+
}
41+
42+
private void visitTagTokenName(PsiElement element) {
43+
String tagName = element.getText();
44+
if (StringUtils.isNotBlank(tagName)) {
45+
// prevent adding multiple "deprecated" message, as we have alias classes
46+
final boolean[] hasDeprecated = {false};
47+
48+
TwigUtil.visitTokenParsers(holder.getProject(), triple -> {
49+
if (hasDeprecated[0]) {
50+
return;
51+
}
52+
53+
String currentTagName = triple.getFirst();
54+
if(tagName.equalsIgnoreCase(currentTagName) || (tagName.toLowerCase().startsWith("end") && currentTagName.equalsIgnoreCase(tagName.substring(3)))) {
55+
PhpClass phpClass = triple.getThird().getContainingClass();
56+
if (phpClass != null && phpClass.isDeprecated()) {
57+
String classDeprecatedMessage = getClassDeprecatedMessage(phpClass);
58+
59+
String message = classDeprecatedMessage != null
60+
? classDeprecatedMessage
61+
: "Deprecated Tag usage";
62+
63+
hasDeprecated[0] = true;
64+
65+
// "Deprecated" highlight is not visible, so we are going here for weak warning
66+
// WEAK_WARNING would be match; but not really visible
67+
holder.registerProblem(element.getParent(), message, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
68+
}
69+
}
70+
});
71+
}
72+
}
73+
74+
@Nullable
75+
private String getClassDeprecatedMessage(@NotNull PhpClass phpClass) {
76+
if (phpClass.isDeprecated()) {
77+
PhpDocComment docComment = phpClass.getDocComment();
78+
if (docComment != null) {
79+
for (PhpDocTag deprecatedTag : docComment.getTagElementsByName("@deprecated")) {
80+
String tagValue = deprecatedTag.getTagValue();
81+
if (StringUtils.isNotBlank(tagValue)) {
82+
return StringUtils.abbreviate("Deprecated: " + tagValue, 100);
83+
}
84+
}
85+
}
86+
}
87+
88+
return null;
89+
}
90+
};
91+
}
92+
}

src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/TwigUtil.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
import fr.adrienbrault.idea.symfony2plugin.util.service.ServiceXmlParserFactory;
6262
import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper;
6363
import icons.TwigIcons;
64+
import kotlin.Triple;
6465
import org.apache.commons.lang.StringUtils;
6566
import org.jetbrains.annotations.NotNull;
6667
import org.jetbrains.annotations.Nullable;
@@ -2515,7 +2516,7 @@ public static DomainScope getTwigFileDomainScope(@NotNull PsiElement psiElement)
25152516
* Visit Twig TokenParser
25162517
* eg. {% my_token %}
25172518
*/
2518-
public static void visitTokenParsers(@NotNull Project project, @NotNull Consumer<Pair<String, PsiElement>> consumer) {
2519+
public static void visitTokenParsers(@NotNull Project project, @NotNull Consumer<Triple<String, PsiElement, Method>> consumer) {
25192520
Set<PhpClass> allSubclasses = new HashSet<>();
25202521

25212522
PhpIndex phpIndex = PhpIndex.getInstance(project);
@@ -2542,7 +2543,7 @@ public static void visitTokenParsers(@NotNull Project project, @NotNull Consumer
25422543
if(returnValue instanceof StringLiteralExpression) {
25432544
String contents = ((StringLiteralExpression) returnValue).getContents();
25442545
if(StringUtils.isNotBlank(contents)) {
2545-
consumer.consume(Pair.create(contents, returnValue));
2546+
consumer.consume(new Triple<>(contents, returnValue, getTag));
25462547
}
25472548
}
25482549
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,12 @@
339339
language="Twig"
340340
implementationClass="fr.adrienbrault.idea.symfony2plugin.templating.inspection.TwigVariableDeprecatedInspection"/>
341341

342+
<localInspection groupPath="Symfony" shortName="TwigExtensionDeprecatedInspection" displayName="Deprecated Twig Extension"
343+
groupName="Twig"
344+
enabledByDefault="true" level="WARNING"
345+
language="Twig"
346+
implementationClass="fr.adrienbrault.idea.symfony2plugin.templating.inspection.TwigExtensionDeprecatedInspection"/>
347+
342348
<localInspection groupPath="Symfony" shortName="YamlQuotedEscapedInspection" displayName="Quoted issues"
343349
groupName="Yaml"
344350
enabledByDefault="true" level="WARNING"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<html>
2+
<body>
3+
<p>Detect Twig token tags which are marked as deprecated via PhpClass</p>
4+
<!-- tooltip end -->
5+
<p>Deprecation inspection for Twig token tags</p>
6+
</body>
7+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package fr.adrienbrault.idea.symfony2plugin.tests.templating.inspection;
2+
3+
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;
4+
5+
/**
6+
* @author Daniel Espendiller <[email protected]>
7+
* @see fr.adrienbrault.idea.symfony2plugin.templating.inspection.TwigExtensionDeprecatedInspection
8+
*/
9+
public class TwigExtensionDeprecatedInspectionTest extends SymfonyLightCodeInsightFixtureTestCase {
10+
public void setUp() throws Exception {
11+
super.setUp();
12+
myFixture.copyFileToProject("TwigExtensionDeprecatedInspection.php");
13+
}
14+
15+
public String getTestDataPath() {
16+
return "src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/inspection/fixtures";
17+
}
18+
19+
public void testThatDeprecatedTwigTokenProvidesDeprecatedMessageFromPhpClass() {
20+
assertLocalInspectionContains(
21+
"test.html.twig",
22+
"{% spac<caret>eless % }",
23+
"Deprecated: Foobar deprecated message"
24+
);
25+
26+
assertLocalInspectionContains(
27+
"test.html.twig",
28+
"{% endspac<caret>eless % }",
29+
"Deprecated: Foobar deprecated message"
30+
);
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Twig\TokenParser {
4+
interface TokenParserInterface {}
5+
6+
/**
7+
* @deprecated Foobar deprecated message
8+
*/
9+
class SpacelessTokenParser implements TokenParserInterface
10+
{
11+
public function getTag()
12+
{
13+
return 'spaceless';
14+
}
15+
}
16+
}

0 commit comments

Comments
 (0)