Skip to content

Commit 13ac25b

Browse files
authored
Merge pull request #1918 from Haehnchen/feature/1285-template-annotation
#1285 migrate template file annotator to inspection and support php attributes
2 parents 2d31843 + fba67cf commit 13ac25b

File tree

13 files changed

+323
-232
lines changed

13 files changed

+323
-232
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package fr.adrienbrault.idea.symfony2plugin.templating.inspection;
2+
3+
import com.intellij.codeInspection.LocalInspectionTool;
4+
import com.intellij.codeInspection.LocalQuickFix;
5+
import com.intellij.codeInspection.ProblemsHolder;
6+
import com.intellij.psi.PsiElement;
7+
import com.intellij.psi.PsiElementVisitor;
8+
import com.intellij.psi.util.PsiTreeUtil;
9+
import com.intellij.util.containers.ContainerUtil;
10+
import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment;
11+
import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag;
12+
import com.jetbrains.php.lang.psi.elements.Method;
13+
import com.jetbrains.php.lang.psi.elements.PhpAttribute;
14+
import com.jetbrains.php.lang.psi.elements.PhpAttributesList;
15+
import com.jetbrains.php.lang.psi.elements.PhpPsiElement;
16+
import de.espend.idea.php.annotation.dict.PhpDocTagAnnotation;
17+
import de.espend.idea.php.annotation.util.AnnotationUtil;
18+
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
19+
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
20+
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
21+
import fr.adrienbrault.idea.symfony2plugin.util.PhpPsiAttributesUtil;
22+
import org.apache.commons.lang.StringUtils;
23+
import org.jetbrains.annotations.NotNull;
24+
import org.jetbrains.annotations.Nullable;
25+
26+
import java.util.ArrayList;
27+
import java.util.Arrays;
28+
import java.util.Collection;
29+
import java.util.HashSet;
30+
31+
/**
32+
* @author Daniel Espendiller <[email protected]>
33+
*/
34+
public class TemplateExistsAnnotationPhpAttributeLocalInspection extends LocalInspectionTool {
35+
@NotNull
36+
public PsiElementVisitor buildVisitor(final @NotNull ProblemsHolder holder, boolean isOnTheFly) {
37+
return new PsiElementVisitor() {
38+
@Override
39+
public void visitElement(@NotNull PsiElement element) {
40+
if (element instanceof PhpDocTag) {
41+
annotate((PhpDocTag) element, holder);
42+
}
43+
44+
if (element instanceof PhpAttribute) {
45+
String fqn = ((PhpAttribute) element).getFQN();
46+
if (fqn != null && PhpElementsUtil.isInstanceOf(element.getProject(), fqn, TwigUtil.TEMPLATE_ANNOTATION_CLASS)) {
47+
annotate((PhpAttribute) element, holder);
48+
}
49+
}
50+
51+
super.visitElement(element);
52+
}
53+
};
54+
}
55+
56+
private void annotate(@NotNull PhpAttribute phpAttribute, @NotNull ProblemsHolder holder) {
57+
Collection<String> templateNames = new HashSet<>();
58+
59+
if (phpAttribute.getArguments().isEmpty()) {
60+
PsiElement phpAttributesList = phpAttribute.getParent();
61+
if (phpAttributesList instanceof PhpAttributesList) {
62+
PsiElement method = phpAttributesList.getParent();
63+
if (method instanceof Method) {
64+
templateNames.addAll(Arrays.asList(TwigUtil.getControllerMethodShortcut((Method) method)));
65+
}
66+
}
67+
} else {
68+
String attributeDefaultValue = PhpPsiAttributesUtil.getAttributeValueByNameAsStringWithDefaultParameterFallback(phpAttribute, "template");
69+
if (attributeDefaultValue != null) {
70+
templateNames.add(attributeDefaultValue);
71+
}
72+
}
73+
74+
if(!templateNames.isEmpty()) {
75+
extracted(phpAttribute, holder, templateNames);
76+
}
77+
}
78+
79+
private void annotate(@NotNull PhpDocTag phpDocTag, @NotNull ProblemsHolder holder) {
80+
if(!Symfony2ProjectComponent.isEnabled(phpDocTag.getProject())) {
81+
return;
82+
}
83+
84+
PhpDocTagAnnotation phpDocAnnotationContainer = AnnotationUtil.getPhpDocAnnotationContainer(phpDocTag);
85+
if (phpDocAnnotationContainer == null || !PhpElementsUtil.isEqualClassName(phpDocAnnotationContainer.getPhpClass(), TwigUtil.TEMPLATE_ANNOTATION_CLASS)) {
86+
return;
87+
}
88+
89+
PhpPsiElement phpDocAttrList = phpDocTag.getFirstPsiChild();
90+
if(phpDocAttrList == null) {
91+
return;
92+
}
93+
94+
Collection<String> templateNames = new HashSet<>();
95+
96+
@Nullable String matcher = AnnotationUtil.getPropertyValueOrDefault(phpDocTag, "template");
97+
if (matcher != null) {
98+
templateNames.add(matcher);
99+
} else {
100+
101+
// find template name on last method
102+
PhpDocComment docComment = PsiTreeUtil.getParentOfType(phpDocTag, PhpDocComment.class);
103+
if(null == docComment) {
104+
return;
105+
}
106+
Method method = PsiTreeUtil.getNextSiblingOfType(docComment, Method.class);
107+
if(null == method) {
108+
return;
109+
}
110+
111+
templateNames.addAll(Arrays.asList(TwigUtil.getControllerMethodShortcut(method)));
112+
}
113+
114+
if(!templateNames.isEmpty()) {
115+
extracted(phpDocTag, holder, templateNames);
116+
}
117+
}
118+
119+
private void extracted(@NotNull PsiElement target, @NotNull ProblemsHolder holder, @NotNull Collection<String> templateNames) {
120+
if(templateNames.size() == 0) {
121+
return;
122+
}
123+
124+
for (String templateName : templateNames) {
125+
if (TwigUtil.getTemplateFiles(holder.getProject(), templateName).size() > 0) {
126+
return;
127+
}
128+
}
129+
130+
// find html target, as this this our first priority for end users condition
131+
String templateName = ContainerUtil.find(templateNames, s -> s.toLowerCase().endsWith(".html.twig"));
132+
133+
// fallback on first item
134+
if(templateName == null) {
135+
templateName = templateNames.iterator().next();
136+
}
137+
138+
Collection<LocalQuickFix> quickFixes = new ArrayList<>();
139+
quickFixes.add(new TemplateCreateByNameLocalQuickFix(templateName));
140+
141+
if (StringUtils.isNotBlank(templateName)) {
142+
quickFixes.add(new TemplateGuessTypoQuickFix(templateName));
143+
}
144+
145+
holder.registerProblem(target, "Twig: Missing Template", quickFixes.toArray(new LocalQuickFix[0]));
146+
}
147+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,11 @@ public static String[] getControllerMethodShortcut(@NotNull Method method) {
211211
shortcutName + ".html.twig",
212212
shortcutName + ".json.twig",
213213
shortcutName + ".xml.twig",
214+
shortcutName + ".text.twig", // emails
214215
shortcutNameForOldNotation + ".html.twig",
215216
shortcutNameForOldNotation + ".json.twig",
216217
shortcutNameForOldNotation + ".xml.twig",
218+
shortcutNameForOldNotation + ".text.twig", // emails
217219
};
218220
}
219221

src/main/java/fr/adrienbrault/idea/symfony2plugin/twig/annotation/TemplateAnnotationAnnotator.java

Lines changed: 0 additions & 104 deletions
This file was deleted.

src/main/java/fr/adrienbrault/idea/symfony2plugin/util/PhpPsiAttributesUtil.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
import java.util.Collection;
1717
import java.util.Collections;
18+
import java.util.List;
1819

1920
/**
2021
* Helpers for PHP 8 Attributes psi access

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
<PhpAnnotationReferenceProvider implementation="fr.adrienbrault.idea.symfony2plugin.doctrine.DoctrineAnnotationTargetEntityReferences"/>
55
<PhpAnnotationReferenceProvider implementation="fr.adrienbrault.idea.symfony2plugin.config.SymfonyAnnotationReferences"/>
66
<PhpAnnotationReferenceProvider implementation="fr.adrienbrault.idea.symfony2plugin.doctrine.DoctrineAnnotationReferencedColumnReferences"/>
7-
<PhpAnnotationDocTagAnnotator implementation="fr.adrienbrault.idea.symfony2plugin.twig.annotation.TemplateAnnotationAnnotator"/>
87
<PhpAnnotationDocTagGotoHandler implementation="fr.adrienbrault.idea.symfony2plugin.twig.annotation.TemplateAnnotationGotoHandler"/>
98

109
<!-- @IsGranted -->

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,13 @@
483483
language="Twig"
484484
implementationClass="fr.adrienbrault.idea.symfony2plugin.templating.inspection.TwigAssetsTagMissingInspection"/>
485485

486+
<localInspection groupPath="Symfony" shortName="TemplateExistsAnnotationPhpAttributeLocalInspection" displayName="Twig: Missing Template"
487+
groupName="Asset"
488+
enabledByDefault="true"
489+
level="WARNING"
490+
language="PHP"
491+
implementationClass="fr.adrienbrault.idea.symfony2plugin.templating.inspection.TemplateExistsAnnotationPhpAttributeLocalInspection"/>
492+
486493
<intentionAction>
487494
<className>fr.adrienbrault.idea.symfony2plugin.intentions.php.PhpServiceIntention</className>
488495
<category>PHP</category>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<html>
2+
<body>
3+
<!-- tooltip end -->
4+
</body>
5+
</html>

0 commit comments

Comments
 (0)