Skip to content

Commit a7643ef

Browse files
committed
Add completion for twig tags of Twig_TokenParserInterface::getTag implementations #457
1 parent bd125c0 commit a7643ef

File tree

5 files changed

+103
-9
lines changed

5 files changed

+103
-9
lines changed

src/fr/adrienbrault/idea/symfony2plugin/TwigHelper.java

+30
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,36 @@ public static ElementPattern<PsiElement> getPrintBlockFunctionPattern(String...
343343
.withLanguage(TwigLanguage.INSTANCE);
344344
}
345345

346+
/**
347+
* Twig tag pattern with some hack
348+
* because we have invalid psi elements after STATEMENT_BLOCK_START
349+
*
350+
* {% <carpet> %}
351+
*/
352+
public static ElementPattern<PsiElement> getTagTokenParserPattern() {
353+
//noinspection unchecked
354+
return PlatformPatterns
355+
.psiElement()
356+
.afterLeafSkipping(
357+
PlatformPatterns.or(
358+
PlatformPatterns.psiElement(PsiWhiteSpace.class),
359+
PlatformPatterns.psiElement(TwigTokenTypes.WHITE_SPACE)
360+
),
361+
PlatformPatterns.or(
362+
PlatformPatterns.psiElement(TwigTokenTypes.STATEMENT_BLOCK_START),
363+
PlatformPatterns.psiElement(PsiErrorElement.class)
364+
)
365+
)
366+
.beforeLeafSkipping(
367+
PlatformPatterns.or(
368+
PlatformPatterns.psiElement(PsiWhiteSpace.class),
369+
PlatformPatterns.psiElement(TwigTokenTypes.WHITE_SPACE)
370+
),
371+
PlatformPatterns.psiElement(TwigTokenTypes.STATEMENT_BLOCK_END)
372+
)
373+
.withLanguage(TwigLanguage.INSTANCE);
374+
}
375+
346376
/**
347377
* {% embed "vertical_boxes_skeleton.twig" %}
348378
*/

src/fr/adrienbrault/idea/symfony2plugin/templating/TwigTemplateCompletionContributor.java

+51-8
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,14 @@
99
import com.intellij.psi.PsiFile;
1010
import com.intellij.psi.PsiManager;
1111
import com.intellij.psi.PsiWhiteSpace;
12+
import com.intellij.psi.util.PsiTreeUtil;
1213
import com.intellij.util.ProcessingContext;
1314
import com.jetbrains.php.PhpIcons;
1415
import com.jetbrains.php.PhpIndex;
15-
import com.jetbrains.php.completion.PhpLookupElement;
16-
import com.jetbrains.php.completion.PhpVariantsUtil;
17-
import com.jetbrains.php.completion.insert.PhpReferenceInsertHandler;
18-
import com.jetbrains.php.lang.psi.elements.Field;
19-
import com.jetbrains.php.lang.psi.elements.Method;
20-
import com.jetbrains.php.lang.psi.elements.PhpClass;
21-
import com.jetbrains.php.lang.psi.elements.PhpNamedElement;
22-
import com.jetbrains.php.lang.psi.stubs.indexes.PhpClassIndex;
16+
import com.jetbrains.php.lang.psi.elements.*;
2317
import com.jetbrains.twig.TwigLanguage;
2418
import com.jetbrains.twig.TwigTokenTypes;
19+
import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons;
2520
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
2621
import fr.adrienbrault.idea.symfony2plugin.TwigHelper;
2722
import fr.adrienbrault.idea.symfony2plugin.asset.dic.AssetDirectoryReader;
@@ -48,6 +43,7 @@
4843
import fr.adrienbrault.idea.symfony2plugin.util.controller.ControllerCompletionProvider;
4944
import fr.adrienbrault.idea.symfony2plugin.util.service.ServiceXmlParserFactory;
5045
import icons.TwigIcons;
46+
import org.apache.commons.lang.StringUtils;
5147
import org.jetbrains.annotations.NotNull;
5248

5349
import java.util.*;
@@ -362,6 +358,12 @@ public void addCompletions(@NotNull CompletionParameters parameters,
362358
new FormThemeCompletionProvider()
363359
);
364360

361+
// {% <carpet> %}
362+
extend(CompletionType.BASIC,
363+
TwigHelper.getTagTokenParserPattern(),
364+
new TagTokenParserCompletionProvider()
365+
);
366+
365367
// {% constant('FOO') %}
366368
extend(
367369
CompletionType.BASIC,
@@ -429,6 +431,47 @@ public void addCompletions(@NotNull CompletionParameters parameters,
429431

430432
}
431433

434+
/**
435+
* Parse all classes that implements Twig_TokenParserInterface::getTag
436+
* and provide completion on string
437+
*/
438+
private static class TagTokenParserCompletionProvider extends CompletionProvider<CompletionParameters> {
439+
@Override
440+
protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext processingContext, @NotNull CompletionResultSet resultSet) {
441+
442+
if(!Symfony2ProjectComponent.isEnabled(parameters.getPosition())) {
443+
return;
444+
}
445+
446+
Collection<PhpClass> allSubclasses = PhpIndex.getInstance(parameters.getPosition().getProject()).getAllSubclasses("\\Twig_TokenParserInterface");
447+
for (PhpClass allSubclass : allSubclasses) {
448+
449+
// we dont want to see test extension like "§"
450+
if(allSubclass.getName().endsWith("Test") || allSubclass.getContainingFile().getVirtualFile().getNameWithoutExtension().endsWith("Test")) {
451+
continue;
452+
}
453+
454+
Method getTag = allSubclass.findMethodByName("getTag");
455+
if(getTag == null) {
456+
continue;
457+
}
458+
459+
// get string return value
460+
PhpReturn childrenOfType = PsiTreeUtil.findChildOfType(getTag, PhpReturn.class);
461+
if(childrenOfType != null) {
462+
PhpPsiElement returnValue = childrenOfType.getFirstPsiChild();
463+
if(returnValue instanceof StringLiteralExpression) {
464+
String contents = ((StringLiteralExpression) returnValue).getContents();
465+
if(StringUtils.isNotBlank(contents)) {
466+
resultSet.addElement(LookupElementBuilder.create(contents).withIcon(Symfony2Icons.SYMFONY));
467+
}
468+
}
469+
}
470+
}
471+
472+
}
473+
}
474+
432475
private class FormThemeCompletionProvider extends CompletionProvider<CompletionParameters> {
433476

434477
@Override

tests/fr/adrienbrault/idea/symfony2plugin/tests/SymfonyLightCodeInsightFixtureTestCase.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import java.util.Arrays;
1515
import java.util.HashSet;
16+
import java.util.List;
1617
import java.util.Set;
1718

1819
public abstract class SymfonyLightCodeInsightFixtureTestCase extends LightCodeInsightFixtureTestCase {
@@ -28,7 +29,12 @@ public void assertCompletionContains(LanguageFileType languageFileType, String c
2829
myFixture.configureByText(languageFileType, configureByText);
2930
myFixture.completeBasic();
3031

31-
assertTrue(myFixture.getLookupElementStrings().containsAll(Arrays.asList(lookupStrings)));
32+
List<String> lookupElements = myFixture.getLookupElementStrings();
33+
for (String s : Arrays.asList(lookupStrings)) {
34+
if(!lookupElements.contains(s)) {
35+
fail(String.format("failed that completion contains %s in %s", s, lookupElements.toString()));
36+
}
37+
}
3238
}
3339

3440
public void assertCompletionNotContains(LanguageFileType languageFileType, String configureByText, String... lookupStrings) {

tests/fr/adrienbrault/idea/symfony2plugin/tests/templating/TwigFilterCompletionContributorTest.java

+8
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,12 @@ public void testTwigExtensionLookupElementPresentable() {
4141
assertCompletionLookupTailEquals(TwigFileType.INSTANCE, "{{ 'test'|<caret> }}", "contextWithoutEnvironment", "()");
4242
}
4343

44+
45+
/**
46+
* @see fr.adrienbrault.idea.symfony2plugin.templating.TwigTemplateCompletionContributor.TagTokenParserCompletionProvider
47+
*/
48+
public void testTagTokenParserCompletionProvider() {
49+
assertCompletionContains(TwigFileType.INSTANCE, "{% <caret> %}", "foo_tag");
50+
}
51+
4452
}

tests/fr/adrienbrault/idea/symfony2plugin/tests/templating/fixtures/TwigFilterExtension.php

+7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ class Twig_SimpleFilter {}
88
class SqlFormatter {
99
public function format() {}
1010
}
11+
12+
interface Twig_TokenParserInterface {}
13+
14+
class FooTokenParser implements Twig_TokenParserInterface
15+
{
16+
public function getTag() { return 'foo_tag'; }
17+
}
1118
}
1219

1320
namespace Doctrine\Bundle\DoctrineBundle\Twig;

0 commit comments

Comments
 (0)