Skip to content

Commit ddf6dc4

Browse files
committed
implement twig block name workaround; need to strip block tag content on prefixmatcher #563, #390, #460
1 parent c52cf5d commit ddf6dc4

File tree

4 files changed

+122
-17
lines changed

4 files changed

+122
-17
lines changed

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

+31-8
Original file line numberDiff line numberDiff line change
@@ -729,24 +729,47 @@ public static ElementPattern<PsiElement> getCompletablePattern() {
729729
.withLanguage(TwigLanguage.INSTANCE);
730730
}
731731

732+
/**
733+
* {% block 'foo' %}
734+
* {% block "foo" %}
735+
* {% block foo %}
736+
*/
732737
public static ElementPattern<PsiElement> getBlockTagPattern() {
733738
//noinspection unchecked
734-
return PlatformPatterns
735-
.psiElement(TwigTokenTypes.IDENTIFIER)
736-
.withParent(
737-
PlatformPatterns.psiElement(TwigElementTypes.BLOCK_TAG)
738-
)
739+
return PlatformPatterns.or(
740+
741+
// {% block "foo" %}
742+
PlatformPatterns
743+
.psiElement(TwigTokenTypes.STRING_TEXT)
739744
.afterLeafSkipping(
740745
PlatformPatterns.or(
741-
PlatformPatterns.psiElement(TwigTokenTypes.LBRACE),
742746
PlatformPatterns.psiElement(PsiWhiteSpace.class),
743747
PlatformPatterns.psiElement(TwigTokenTypes.WHITE_SPACE),
744748
PlatformPatterns.psiElement(TwigTokenTypes.SINGLE_QUOTE),
745749
PlatformPatterns.psiElement(TwigTokenTypes.DOUBLE_QUOTE)
746750
),
747-
PlatformPatterns.psiElement(TwigTokenTypes.TAG_NAME).withText("block")
751+
PlatformPatterns.psiElement(TwigTokenTypes.TAG_NAME)
748752
)
749-
.withLanguage(TwigLanguage.INSTANCE);
753+
.withParent(
754+
PlatformPatterns.psiElement(TwigBlockTag.class)
755+
)
756+
.withLanguage(TwigLanguage.INSTANCE),
757+
758+
// {% block foo %}
759+
PlatformPatterns
760+
.psiElement(TwigTokenTypes.IDENTIFIER)
761+
.afterLeafSkipping(
762+
PlatformPatterns.or(
763+
PlatformPatterns.psiElement(PsiWhiteSpace.class),
764+
PlatformPatterns.psiElement(TwigTokenTypes.WHITE_SPACE)
765+
),
766+
PlatformPatterns.psiElement(TwigTokenTypes.TAG_NAME)
767+
)
768+
.withParent(
769+
PlatformPatterns.psiElement(TwigBlockTag.class)
770+
)
771+
.withLanguage(TwigLanguage.INSTANCE)
772+
);
750773
}
751774

752775
/**

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

+19-9
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import com.intellij.codeInsight.completion.*;
44
import com.intellij.codeInsight.lookup.LookupElementBuilder;
55
import com.intellij.openapi.project.Project;
6-
import com.intellij.openapi.vfs.VirtualFile;
76
import com.intellij.patterns.PlatformPatterns;
87
import com.intellij.psi.PsiElement;
98
import com.intellij.psi.PsiFile;
@@ -605,24 +604,35 @@ public void addCompletions(@NotNull CompletionParameters parameters,
605604
}
606605
}
607606

608-
609607
class BlockCompletionProvider extends CompletionProvider<CompletionParameters> {
610608
public void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet resultSet) {
611609

612610
if(!Symfony2ProjectComponent.isEnabled(parameters.getPosition())) {
613611
return;
614612
}
615613

616-
Map<String, VirtualFile> twigFilesByName = TwigHelper.getTwigFilesByName(parameters.getPosition().getProject());
617-
List<TwigBlock> blocks = new TwigBlockParser(twigFilesByName).walk(parameters.getPosition().getContainingFile());
618-
List<String> uniqueList = new ArrayList<String>();
614+
// wtf: need to prefix the block tag itself. remove this behavior and strip for new Matcher
615+
// Find first Identifier "b" char or fallback to empty:
616+
// "{% block b", "{% block"
617+
String blockNamePrefix = resultSet.getPrefixMatcher().getPrefix();
618+
int spacePos = blockNamePrefix.lastIndexOf(' ');
619+
blockNamePrefix = spacePos > 0 ? blockNamePrefix.substring(spacePos + 1) : "";
620+
CompletionResultSet myResultSet = resultSet.withPrefixMatcher(blockNamePrefix);
621+
622+
// collect blocks in all related files
623+
List<TwigBlock> blocks = new TwigBlockParser(
624+
TwigHelper.getTwigFilesByName(parameters.getPosition().getProject())
625+
).walk(parameters.getPosition().getContainingFile());
626+
627+
Set<String> uniqueList = new HashSet<String>();
619628
for (TwigBlock block : blocks) {
620-
if(!uniqueList.contains(block.getName())) {
621-
uniqueList.add(block.getName());
622-
resultSet.addElement(new TwigBlockLookupElement(block));
629+
if(uniqueList.contains(block.getName())) {
630+
continue;
623631
}
624-
}
625632

633+
uniqueList.add(block.getName());
634+
myResultSet.addElement(new TwigBlockLookupElement(block));
635+
}
626636
}
627637
}
628638

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

+19
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,25 @@ public void testIncludeTagNonAllowedTags() {
124124
assertSize(0, getIncludeTemplates("{% import 'foo.html.twig' %}", TwigElementTypes.IMPORT_TAG));
125125
}
126126

127+
/**
128+
* @see TwigHelper#getBlockTagPattern
129+
*/
130+
public void testGetBlockTagPattern() {
131+
String[] blocks = {
132+
"{% block 'a<caret>a' %}",
133+
"{% block \"a<caret>a\" %}",
134+
"{% block a<caret>a %}"
135+
};
136+
137+
for (String s : blocks) {
138+
myFixture.configureByText(TwigFileType.INSTANCE, s);
139+
140+
assertTrue(TwigHelper.getBlockTagPattern().accepts(
141+
myFixture.getFile().findElementAt(myFixture.getCaretOffset()))
142+
);
143+
}
144+
}
145+
127146
private void assertEqual(Collection<String> c, String... values) {
128147
if(!StringUtils.join(c, ",").equals(StringUtils.join(Arrays.asList(values), ","))) {
129148
fail(String.format("Fail that '%s' is equal '%s'", StringUtils.join(c, ","), StringUtils.join(Arrays.asList(values), ",")));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package fr.adrienbrault.idea.symfony2plugin.tests.templating;
2+
3+
import com.intellij.openapi.application.ApplicationManager;
4+
import com.intellij.openapi.vfs.VfsUtil;
5+
import com.intellij.openapi.vfs.VirtualFile;
6+
import com.jetbrains.twig.TwigFileType;
7+
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;
8+
9+
import java.io.IOException;
10+
11+
/**
12+
* @author Daniel Espendiller <daniel@espendiller.net>
13+
* @see com.jetbrains.twig.completion.TwigCompletionContributor
14+
*/
15+
public class TwigTemplateCompletionContributorTest extends SymfonyLightCodeInsightFixtureTestCase {
16+
17+
public void setUp() throws Exception {
18+
super.setUp();
19+
}
20+
21+
/**
22+
* @see fr.adrienbrault.idea.symfony2plugin.templating.TwigTemplateCompletionContributor
23+
*/
24+
public void testBlockCompletion() {
25+
if(System.getenv("PHPSTORM_ENV") != null) return;
26+
27+
try {
28+
createDummyFiles(
29+
"app/Resources/views/block.html.twig"
30+
);
31+
} catch (Exception e) {
32+
e.printStackTrace();
33+
}
34+
35+
// build pseudo file with block
36+
final VirtualFile relativeFile = VfsUtil.findRelativeFile(getProject().getBaseDir(), "app/Resources/views/block.html.twig".split("/"));
37+
ApplicationManager.getApplication().runWriteAction(new Runnable() {
38+
@Override
39+
public void run() {
40+
try {
41+
relativeFile.setBinaryContent("{% block foo %}{% endblock %}".getBytes());
42+
} catch (IOException e) {
43+
e.printStackTrace();
44+
}
45+
relativeFile.refresh(false, false);
46+
}
47+
});
48+
49+
assertCompletionContains(TwigFileType.INSTANCE, "{% extends '::block.html.twig' %}{% block <caret> %}", "foo");
50+
assertCompletionContains(TwigFileType.INSTANCE, "{% extends '::block.html.twig' %}{% block \"<caret>\" %}", "foo");
51+
assertCompletionContains(TwigFileType.INSTANCE, "{% extends '::block.html.twig' %}{% block '<caret>' %}", "foo");
52+
}
53+
}

0 commit comments

Comments
 (0)