Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit fc3c1ea

Browse files
Merge pull request #2433 from Haehnchen/feature/twig-extension-attributes
add support for Twig extension registered via Attributes
2 parents c58958a + bd200de commit fc3c1ea

File tree

8 files changed

+226
-7
lines changed

8 files changed

+226
-7
lines changed
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package fr.adrienbrault.idea.symfony2plugin.stubs.indexes;
2+
3+
import com.intellij.util.indexing.*;
4+
import com.intellij.util.io.DataExternalizer;
5+
import com.intellij.util.io.EnumeratorStringDescriptor;
6+
import com.intellij.util.io.KeyDescriptor;
7+
import com.jetbrains.php.lang.PhpFileType;
8+
import com.jetbrains.php.lang.psi.PhpFile;
9+
import com.jetbrains.php.lang.psi.PhpPsiUtil;
10+
import com.jetbrains.php.lang.psi.elements.*;
11+
import fr.adrienbrault.idea.symfony2plugin.util.PhpPsiAttributesUtil;
12+
import org.apache.commons.lang3.StringUtils;
13+
import org.jetbrains.annotations.NotNull;
14+
15+
import java.util.HashMap;
16+
import java.util.Map;
17+
18+
/**
19+
* @author Daniel Espendiller <daniel@espendiller.net>
20+
*/
21+
public class TwigAttributeIndex extends FileBasedIndexExtension<String, String> {
22+
public static final ID<String, String> KEY = ID.create("fr.adrienbrault.idea.symfony2plugin.twig_attribute_extension.index");
23+
24+
@Override
25+
public @NotNull ID<String, String> getName() {
26+
return KEY;
27+
}
28+
29+
@Override
30+
public @NotNull DataIndexer<String, String, FileContent> getIndexer() {
31+
return new TwigAttributeIndexer();
32+
}
33+
34+
@Override
35+
public @NotNull KeyDescriptor<String> getKeyDescriptor() {
36+
return EnumeratorStringDescriptor.INSTANCE;
37+
}
38+
39+
@Override
40+
public @NotNull DataExternalizer<String> getValueExternalizer() {
41+
return EnumeratorStringDescriptor.INSTANCE;
42+
}
43+
44+
@Override
45+
public @NotNull FileBasedIndex.InputFilter getInputFilter() {
46+
return virtualFile -> virtualFile.getFileType() == PhpFileType.INSTANCE;
47+
}
48+
49+
@Override
50+
public boolean dependsOnFileContent() {
51+
return true;
52+
}
53+
54+
@Override
55+
public int getVersion() {
56+
return 1;
57+
}
58+
59+
public static class TwigAttributeIndexer implements DataIndexer<String, String, FileContent> {
60+
private static final String[] SUPPORTED_ATTRIBUTES = {
61+
"\\Twig\\Attribute\\AsTwigFilter",
62+
"\\Twig\\Attribute\\AsTwigFunction",
63+
"\\Twig\\Attribute\\AsTwigTest",
64+
};
65+
66+
@Override
67+
public @NotNull Map<String, String> map(@NotNull FileContent inputData) {
68+
Map<String, String> result = new HashMap<>();
69+
if (!(inputData.getPsiFile() instanceof PhpFile phpFile)) {
70+
return result;
71+
}
72+
73+
for (PhpClass phpClass : PhpPsiUtil.findAllClasses(phpFile)) {
74+
for (Method method : phpClass.getOwnMethods()) {
75+
processMethodAttributes(phpClass, method, result);
76+
}
77+
}
78+
79+
return result;
80+
}
81+
82+
private void processMethodAttributes(@NotNull PhpClass phpClass, @NotNull Method method, @NotNull Map<String, String> result) {
83+
for (PhpAttribute attribute : method.getAttributes()) {
84+
String attributeFqn = attribute.getFQN();
85+
if (attributeFqn == null) {
86+
continue;
87+
}
88+
89+
for (String supportedAttr : SUPPORTED_ATTRIBUTES) {
90+
if (supportedAttr.equals(attributeFqn)) {
91+
String nameAttribute = PhpPsiAttributesUtil.getAttributeValueByNameAsString(attribute, 0, "name");
92+
if (nameAttribute != null) {
93+
result.put(nameAttribute, String.format("#M#C\\%s.%s", StringUtils.stripStart(phpClass.getFQN(), "\\"), method.getName()));
94+
}
95+
96+
break;
97+
}
98+
}
99+
}
100+
}
101+
}
102+
}

‎src/main/java/fr/adrienbrault/idea/symfony2plugin/stubs/util/IndexUtil.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ public static void forceReindex() {
3030
TwigIncludeStubIndex.KEY,
3131
TwigMacroFunctionStubIndex.KEY,
3232
TranslationStubIndex.KEY,
33-
TwigBlockIndexExtension.KEY
33+
TwigBlockIndexExtension.KEY,
34+
TwigAttributeIndex.KEY
3435
};
3536

3637
for(ID<?,?> id: indexIds) {

‎src/main/java/fr/adrienbrault/idea/symfony2plugin/templating/util/TwigExtensionParser.java‎

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@
44
import com.intellij.openapi.util.Key;
55
import com.intellij.patterns.PlatformPatterns;
66
import com.intellij.psi.PsiElement;
7+
import com.intellij.psi.search.GlobalSearchScope;
78
import com.intellij.psi.util.*;
89
import com.jetbrains.php.PhpIcons;
910
import com.jetbrains.php.PhpIndex;
11+
import com.jetbrains.php.lang.PhpFileType;
1012
import com.jetbrains.php.lang.parser.PhpElementTypes;
1113
import com.jetbrains.php.lang.psi.PhpPsiUtil;
1214
import com.jetbrains.php.lang.psi.elements.*;
15+
import fr.adrienbrault.idea.symfony2plugin.stubs.cache.FileIndexCaches;
16+
import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.TwigAttributeIndex;
1317
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigExtension;
1418
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
1519
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
@@ -30,6 +34,15 @@ public class TwigExtensionParser {
3034
private static final Key<CachedValue<Map<String, TwigExtension>>> FILTERS_CACHE = new Key<>("TWIG_EXTENSIONS_FILTERS");
3135
private static final Key<CachedValue<Map<String, TwigExtension>>> OPERATORS_CACHE = new Key<>("TWIG_EXTENSIONS_OPERATORS");
3236

37+
private static final Key<CachedValue<Map<String, List<String>>>> TWIG_ATTRIBUTE_FUNCTION_INDEX = new Key<>("TWIG_ATTRIBUTE_FUNCTION_INDEX");
38+
private static final Key<CachedValue<Set<String>>> TWIG_ATTRIBUTE_FUNCTION_INDEX_NAMES = new Key<>("TWIG_ATTRIBUTE_FUNCTION_INDEX_NAMES");
39+
40+
private static final Key<CachedValue<Map<String, List<String>>>> TWIG_ATTRIBUTE_FILTER_INDEX = new Key<>("TWIG_ATTRIBUTE_FILTER_INDEX");
41+
private static final Key<CachedValue<Set<String>>> TWIG_ATTRIBUTE_FILTER_NAMES = new Key<>("TWIG_ATTRIBUTE_FILTER_NAMES");
42+
43+
private static final Key<CachedValue<Map<String, List<String>>>> TWIG_ATTRIBUTE_TEST_INDEX = new Key<>("TWIG_ATTRIBUTE_TEST_INDEX");
44+
private static final Key<CachedValue<Set<String>>> TWIG_ATTRIBUTE_TEST_NAMES = new Key<>("TWIG_ATTRIBUTE_FUNCTION_FILTER_NAMES");
45+
3346
public enum TwigExtensionType {
3447
FUNCTION_METHOD, FUNCTION_NODE, SIMPLE_FUNCTION, FILTER, SIMPLE_TEST, OPERATOR
3548
}
@@ -39,7 +52,7 @@ public static Map<String, TwigExtension> getFunctions(@NotNull Project project)
3952
return CachedValuesManager.getManager(project).getCachedValue(
4053
project,
4154
FUNCTION_CACHE,
42-
() -> CachedValueProvider.Result.create(parseFunctions(TwigUtil.getTwigExtensionClasses(project)), PsiModificationTracker.MODIFICATION_COUNT),
55+
() -> CachedValueProvider.Result.create(parseFunctions(project, TwigUtil.getTwigExtensionClasses(project)), PsiModificationTracker.MODIFICATION_COUNT),
4356
false
4457
);
4558
}
@@ -49,7 +62,7 @@ public static Map<String, TwigExtension> getFilters(@NotNull Project project) {
4962
return CachedValuesManager.getManager(project).getCachedValue(
5063
project,
5164
FILTERS_CACHE,
52-
() -> CachedValueProvider.Result.create(parseFilters(TwigUtil.getTwigExtensionClasses(project)), PsiModificationTracker.MODIFICATION_COUNT),
65+
() -> CachedValueProvider.Result.create(parseFilters(project, TwigUtil.getTwigExtensionClasses(project)), PsiModificationTracker.MODIFICATION_COUNT),
5366
false
5467
);
5568
}
@@ -59,7 +72,7 @@ public static Map<String, TwigExtension> getSimpleTest(@NotNull Project project)
5972
return CachedValuesManager.getManager(project).getCachedValue(
6073
project,
6174
TEST_CACHE,
62-
() -> CachedValueProvider.Result.create(parseTests(TwigUtil.getTwigExtensionClasses(project)), PsiModificationTracker.MODIFICATION_COUNT),
75+
() -> CachedValueProvider.Result.create(parseTests(project, TwigUtil.getTwigExtensionClasses(project)), PsiModificationTracker.MODIFICATION_COUNT),
6376
false
6477
);
6578
}
@@ -75,7 +88,7 @@ public static Map<String, TwigExtension> getOperators(@NotNull Project project)
7588
}
7689

7790
@NotNull
78-
private static Map<String, TwigExtension> parseFilters(@NotNull Collection<PhpClass> phpClasses) {
91+
private static Map<String, TwigExtension> parseFilters(@NotNull Projectproject, @NotNullCollection<PhpClass> phpClasses) {
7992
Map<String, TwigExtension> extensions = new HashMap<>();
8093

8194
for (PhpClass phpClass : phpClasses) {
@@ -92,11 +105,15 @@ private static Map<String, TwigExtension> parseFilters(@NotNull Collection<PhpCl
92105
}
93106
}
94107

108+
for (Map.Entry<String, List<String>> entry : FileIndexCaches.getStringDataCache(project, TWIG_ATTRIBUTE_FILTER_INDEX, TWIG_ATTRIBUTE_FILTER_NAMES, TwigAttributeIndex.KEY, GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(project), PhpFileType.INSTANCE)).entrySet()) {
109+
extensions.put(entry.getKey(), new TwigExtension(TwigExtensionType.FILTER, entry.getValue().getFirst()));
110+
}
111+
95112
return Collections.unmodifiableMap(extensions);
96113
}
97114

98115
@NotNull
99-
private static Map<String, TwigExtension> parseFunctions(@NotNull Collection<PhpClass> phpClasses) {
116+
private static Map<String, TwigExtension> parseFunctions(@NotNull Projectproject, @NotNullCollection<PhpClass> phpClasses) {
100117
Map<String, TwigExtension> extensions = new HashMap<>();
101118

102119
for (PhpClass phpClass : phpClasses) {
@@ -113,11 +130,15 @@ private static Map<String, TwigExtension> parseFunctions(@NotNull Collection<Php
113130
}
114131
}
115132

133+
for (Map.Entry<String, List<String>> entry : FileIndexCaches.getStringDataCache(project, TWIG_ATTRIBUTE_FUNCTION_INDEX, TWIG_ATTRIBUTE_FUNCTION_INDEX_NAMES, TwigAttributeIndex.KEY, GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(project), PhpFileType.INSTANCE)).entrySet()) {
134+
extensions.put(entry.getKey(), new TwigExtension(TwigExtensionType.FUNCTION_METHOD, entry.getValue().getFirst()));
135+
}
136+
116137
return Collections.unmodifiableMap(extensions);
117138
}
118139

119140
@NotNull
120-
private static Map<String, TwigExtension> parseTests(@NotNull Collection<PhpClass> phpClasses) {
141+
private static Map<String, TwigExtension> parseTests(@NotNull Projectproject, @NotNullCollection<PhpClass> phpClasses) {
121142
Map<String, TwigExtension> extensions = new HashMap<>();
122143

123144
for (PhpClass phpClass : phpClasses) {
@@ -129,6 +150,10 @@ private static Map<String, TwigExtension> parseTests(@NotNull Collection<PhpClas
129150
}
130151
}
131152

153+
for (Map.Entry<String, List<String>> entry : FileIndexCaches.getStringDataCache(project, TWIG_ATTRIBUTE_TEST_INDEX, TWIG_ATTRIBUTE_TEST_NAMES, TwigAttributeIndex.KEY, GlobalSearchScope.getScopeRestrictedByFileTypes(GlobalSearchScope.allScope(project), PhpFileType.INSTANCE)).entrySet()) {
154+
extensions.put(entry.getKey(), new TwigExtension(TwigExtensionType.SIMPLE_TEST, entry.getValue().getFirst()));
155+
}
156+
132157
return Collections.unmodifiableMap(extensions);
133158
}
134159

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@
270270
<fileBasedIndex implementation="fr.adrienbrault.idea.symfony2plugin.stubs.indexes.UxTemplateStubIndex"/>
271271
<fileBasedIndex implementation="fr.adrienbrault.idea.symfony2plugin.stubs.indexes.TwigBlockEmbedIndex"/>
272272
<fileBasedIndex implementation="fr.adrienbrault.idea.symfony2plugin.stubs.indexes.ConfigStubIndex"/>
273+
<fileBasedIndex implementation="fr.adrienbrault.idea.symfony2plugin.stubs.indexes.TwigAttributeIndex"/>
273274

274275
<codeInsight.lineMarkerProvider language="PHP" implementationClass="fr.adrienbrault.idea.symfony2plugin.config.ServiceLineMarkerProvider"/>
275276
<codeInsight.lineMarkerProvider language="PHP" implementationClass="fr.adrienbrault.idea.symfony2plugin.dic.ControllerMethodLineMarkerProvider"/>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package fr.adrienbrault.idea.symfony2plugin.tests.stubs.indexes;
2+
3+
import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.TwigAttributeIndex;
4+
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;
5+
6+
/**
7+
* @author Daniel Espendiller <daniel@espendiller.net>
8+
* @see fr.adrienbrault.idea.symfony2plugin.stubs.indexes.TwigAttributeIndex
9+
*/
10+
public class TwigAttributeIndexTest extends SymfonyLightCodeInsightFixtureTestCase {
11+
public void setUp() throws Exception {
12+
super.setUp();
13+
14+
myFixture.copyFileToProject("TwigAttributeIndex.php");
15+
}
16+
17+
public void testThatValuesAreInIndex() {
18+
assertIndexContainsKeyWithValue(TwigAttributeIndex.KEY, "product_number_filter", "#M#C\\App\\Twig\\AppExtension.formatProductNumberFilter");
19+
assertIndexContainsKeyWithValue(TwigAttributeIndex.KEY, "product_number_function", "#M#C\\App\\Twig\\AppExtension.formatProductNumberFunction");
20+
assertIndexContainsKeyWithValue(TwigAttributeIndex.KEY, "product_number_test", "#M#C\\App\\Twig\\AppExtension.formatProductNumberTest");
21+
}
22+
23+
public String getTestDataPath() {
24+
return "src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/fixtures";
25+
}
26+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace App\Twig;
4+
5+
use Twig\Attribute\AsTwigFilter;
6+
use Twig\Attribute\AsTwigFunction;
7+
use Twig\Attribute\AsTwigTest;
8+
9+
class AppExtension
10+
{
11+
#[AsTwigFilter('product_number_filter')]
12+
public function formatProductNumberFilter(string $number): string
13+
{
14+
}
15+
16+
#[AsTwigFunction('product_number_function')]
17+
public function formatProductNumberFunction(string $number): string
18+
{
19+
}
20+
21+
#[AsTwigTest('product_number_test')]
22+
public function formatProductNumberTest(string $number): string
23+
{
24+
}
25+
}

‎src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/util/TwigExtensionParserTest.java‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,21 @@ public void testExtensionAreCollected() {
7676
"#Fmax",
7777
TwigExtensionParser.getFunctions(getProject()).get("conditional_return").getSignature()
7878
);
79+
80+
assertEquals(
81+
"#M#C\\App\\Twig\\AppExtension.formatProductNumberFilter",
82+
TwigExtensionParser.getFilters(getProject()).get("product_number_filter").getSignature()
83+
);
84+
85+
assertEquals(
86+
"#M#C\\App\\Twig\\AppExtension.formatProductNumberFunction",
87+
TwigExtensionParser.getFunctions(getProject()).get("product_number_function").getSignature()
88+
);
89+
90+
assertEquals(
91+
"#M#C\\App\\Twig\\AppExtension.formatProductNumberTest",
92+
TwigExtensionParser.getSimpleTest(getProject()).get("product_number_test").getSignature()
93+
);
7994
}
8095

8196
public void testExtensionAreCollectedForDeprecated() {

‎src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/templating/util/fixtures/twig_extensions.php‎

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,28 @@ public function foobar()
147147
namespace Twig\Extension {
148148
interface ExtensionInterface {}
149149
class AbstractExtension implements ExtensionInterface {}
150+
}
151+
152+
namespace App\Twig {
153+
use Twig\Attribute\AsTwigFilter;
154+
use Twig\Attribute\AsTwigFunction;
155+
use Twig\Attribute\AsTwigTest;
156+
157+
class AppExtension
158+
{
159+
#[AsTwigFilter('product_number_filter')]
160+
public function formatProductNumberFilter(string $number): string
161+
{
162+
}
163+
164+
#[AsTwigFunction('product_number_function')]
165+
public function formatProductNumberFunction(string $number): string
166+
{
167+
}
168+
169+
#[AsTwigTest('product_number_test')]
170+
public function formatProductNumberTest(string $number): string
171+
{
172+
}
173+
}
150174
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /