Skip to content

Commit 93031d0

Browse files
committed
blocks in Twig print block supported by linemarker and navigation
1 parent 81a3096 commit 93031d0

File tree

4 files changed

+106
-75
lines changed

4 files changed

+106
-75
lines changed

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

Lines changed: 69 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2108,51 +2108,56 @@ private static Collection<String> getTernaryStrings(@NotNull PsiElement psiQuest
21082108
/**
21092109
* Collect all block names in file
21102110
*
2111-
* {% block sds %}, {% block 'sds' %}, {% block "sds" %}
2112-
* {%- block sds -%}
2111+
* {% block sds %}, {% block 'sds' %}, {% block "sds" %}, {%- block sds -%}
2112+
* {{ block('foobar') }}
21132113
*/
21142114
@NotNull
21152115
public static Collection<TwigBlock> getBlocksInFile(@NotNull TwigFile twigFile) {
2116+
// prefilter elements; dont visit until leaf elements fpr performance
2117+
PsiElement[] blocks = PsiTreeUtil.collectElements(twigFile, psiElement ->
2118+
psiElement instanceof TwigBlockTag || (psiElement instanceof TwigCompositeElement && psiElement.getNode().getElementType() == TwigElementTypes.PRINT_BLOCK)
2119+
);
21162120

21172121
Collection<TwigBlock> block = new ArrayList<>();
21182122

2119-
PsiElementPattern.Capture<PsiElement> pattern = null;
2123+
for (PsiElement psiElement : blocks) {
2124+
final PsiElement[] target = new PsiElement[1];
21202125

2121-
for (TwigBlockTag twigBlockTag : PsiTreeUtil.collectElementsOfType(twigFile, TwigBlockTag.class)) {
2126+
if(psiElement instanceof TwigBlockTag) {
2127+
// {% block sds %}, {% block "sds" %}
2128+
psiElement.acceptChildren(new PsiRecursiveElementVisitor() {
2129+
@Override
2130+
public void visitElement(PsiElement element) {
2131+
if(target[0] == null && getBlockTagPattern().accepts(element)) {
2132+
target[0] = element;
2133+
}
2134+
super.visitElement(element);
2135+
}
2136+
});
21222137

2123-
String name = twigBlockTag.getName();
2124-
if(name != null && StringUtils.isNotBlank(name)) {
2125-
block.add(new TwigBlock(name, twigBlockTag));
2138+
} else if(psiElement instanceof TwigCompositeElement) {
2139+
// {{ block('foobar') }}
2140+
psiElement.acceptChildren(new PsiRecursiveElementVisitor() {
2141+
@Override
2142+
public void visitElement(PsiElement element) {
2143+
if(target[0] == null && getPrintBlockFunctionPattern("block").accepts(element)) {
2144+
target[0] = element;
2145+
}
2146+
super.visitElement(element);
2147+
}
2148+
});
21262149
}
21272150

2128-
PsiElement firstChild = twigBlockTag.getFirstChild();
2129-
if(firstChild == null) {
2151+
if(target[0] == null) {
21302152
continue;
21312153
}
21322154

2133-
// provide support for quote wrapping
2134-
// {% block 'sds' %}
2135-
if(pattern == null) {
2136-
pattern = PlatformPatterns.psiElement(TwigTokenTypes.STRING_TEXT)
2137-
.afterLeafSkipping(
2138-
PlatformPatterns.or(
2139-
PlatformPatterns.psiElement(PsiWhiteSpace.class),
2140-
PlatformPatterns.psiElement(TwigTokenTypes.WHITE_SPACE),
2141-
PlatformPatterns.psiElement(TwigTokenTypes.DOUBLE_QUOTE),
2142-
PlatformPatterns.psiElement(TwigTokenTypes.SINGLE_QUOTE)
2143-
),
2144-
PlatformPatterns.psiElement(TwigTokenTypes.TAG_NAME)
2145-
);
2146-
}
2147-
2148-
PsiElement psiString = PsiElementUtils.getNextSiblingOfType(firstChild, pattern);
2149-
if(psiString != null) {
2150-
String text = psiString.getText();
2151-
if(StringUtils.isNotBlank(text)) {
2152-
block.add(new TwigBlock(text, twigBlockTag));
2153-
}
2155+
String blockName = target[0].getText();
2156+
if(StringUtils.isBlank(blockName)) {
2157+
continue;
21542158
}
21552159

2160+
block.add(new TwigBlock(blockName, target[0]));
21562161
}
21572162

21582163
return block;
@@ -2333,6 +2338,40 @@ public static String getDomainFromTranslationTag(@NotNull TwigCompositeElement t
23332338
return text;
23342339
}
23352340

2341+
/**
2342+
* {% extends 'foobar.html.twig' %}
2343+
*
2344+
* {{ block('foo<caret>bar') }}
2345+
* {% block 'foo<caret>bar' %}
2346+
* {% block foo<caret>bar %}
2347+
*/
2348+
@NotNull
2349+
public static Collection<PsiElement> getBlocksByImplementations(@NotNull PsiElement blockPsiName, @NotNull TemplateFileMap fileMap) {
2350+
PsiFile psiFile = blockPsiName.getContainingFile();
2351+
if(psiFile == null) {
2352+
return Collections.emptyList();
2353+
}
2354+
2355+
Collection<PsiFile> twigChild = TwigUtil.getTemplateFileReferences(psiFile, fileMap);
2356+
if(twigChild.size() == 0) {
2357+
return Collections.emptyList();
2358+
}
2359+
2360+
String blockName = blockPsiName.getText();
2361+
if(StringUtils.isBlank(blockName)) {
2362+
return Collections.emptyList();
2363+
}
2364+
2365+
Collection<PsiElement> blockTargets = new ArrayList<>();
2366+
for(PsiFile psiFile1: twigChild) {
2367+
blockTargets.addAll(Arrays.asList(PsiTreeUtil.collectElements(psiFile1, psiElement1 ->
2368+
(TwigHelper.getBlockTagPattern().accepts(psiElement1) || TwigHelper.getPrintBlockFunctionPattern("block").accepts(psiElement1)) && blockName.equals(psiElement1.getText())))
2369+
);
2370+
}
2371+
2372+
return blockTargets;
2373+
}
2374+
23362375
private static class MyLimitedVirtualFileVisitor extends VirtualFileVisitor {
23372376
@NotNull
23382377
private final TwigPathContentIterator twigPathContentIterator;

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.jetbrains.annotations.NotNull;
1717

1818
import java.util.*;
19+
import java.util.stream.Collectors;
1920

2021
/**
2122
* @author Daniel Espendiller <daniel@espendiller.net>
@@ -35,23 +36,31 @@ public void register(GotoCompletionRegistrarParameter registrar) {
3536
}
3637

3738
private static class BlockFunctionReferenceCompletionProvider extends GotoCompletionProvider {
38-
39-
public BlockFunctionReferenceCompletionProvider(@NotNull PsiElement element) {
39+
private BlockFunctionReferenceCompletionProvider(@NotNull PsiElement element) {
4040
super(element);
4141
}
4242

4343
@NotNull
4444
public Collection<PsiElement> getPsiTargets(PsiElement element) {
45-
4645
String blockName = PsiElementUtils.trimQuote(element.getText());
4746
if(StringUtils.isBlank(blockName)) {
4847
return Collections.emptyList();
4948
}
5049

51-
return Arrays.asList(
50+
Collection<PsiElement> psiElements = new HashSet<>();
51+
52+
psiElements.addAll(Arrays.asList(
5253
TwigTemplateGoToDeclarationHandler.getBlockNameGoTo(element.getContainingFile(), blockName, true)
54+
));
55+
56+
psiElements.addAll(
57+
TwigHelper.getBlocksByImplementations(element, TwigHelper.getTemplateMap(element.getProject(), true, false))
5358
);
5459

60+
// filter self navigation
61+
return psiElements.stream()
62+
.filter(psiElement -> psiElement != element)
63+
.collect(Collectors.toSet());
5564
}
5665

5766
@NotNull

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

Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -55,22 +55,6 @@ public void collectSlowLineMarkers(@NotNull List<PsiElement> psiElements, @NotNu
5555
}
5656

5757
for(PsiElement psiElement: psiElements) {
58-
59-
// blocks
60-
if (TwigHelper.getBlockTagPattern().accepts(psiElement)) {
61-
62-
LineMarkerInfo lineImpl = this.attachBlockImplements(psiElement);
63-
if(lineImpl != null) {
64-
results.add(lineImpl);
65-
}
66-
67-
LineMarkerInfo lineOverwrites = this.attachBlockOverwrites(psiElement);
68-
if(lineOverwrites != null) {
69-
results.add(lineOverwrites);
70-
}
71-
72-
}
73-
7458
// controller
7559
if(psiElement instanceof TwigFile) {
7660
attachController((TwigFile) psiElement, results);
@@ -87,13 +71,23 @@ public void collectSlowLineMarkers(@NotNull List<PsiElement> psiElements, @NotNu
8771
if(overwrites != null) {
8872
results.add(overwrites);
8973
}
90-
}
74+
} else if (TwigHelper.getBlockTagPattern().accepts(psiElement) || TwigHelper.getPrintBlockFunctionPattern("block").accepts(psiElement)) {
75+
// blocks: {% block 'foobar' %}, {{ block('foobar') }}
76+
77+
LineMarkerInfo lineImpl = this.attachBlockImplements(psiElement);
78+
if(lineImpl != null) {
79+
results.add(lineImpl);
80+
}
9181

82+
LineMarkerInfo lineOverwrites = this.attachBlockOverwrites(psiElement);
83+
if(lineOverwrites != null) {
84+
results.add(lineOverwrites);
85+
}
86+
}
9287
}
9388

9489
// reset cache
9590
templateMapCache = null;
96-
9791
}
9892

9993
private void attachController(@NotNull TwigFile twigFile, @NotNull Collection<? super RelatedItemLineMarkerInfo> result) {
@@ -210,30 +204,10 @@ private LineMarkerInfo getRelatedPopover(String singleItemTitle, String singleIt
210204
}
211205

212206
@Nullable
213-
private LineMarkerInfo attachBlockImplements(final PsiElement psiElement) {
214-
PsiFile psiFile = psiElement.getContainingFile();
215-
if(psiFile == null) {
216-
return null;
217-
}
218-
207+
private LineMarkerInfo attachBlockImplements(@NotNull PsiElement psiElement) {
219208
TemplateFileMap files = getTemplateFilesByName(psiElement.getProject());
220209

221-
Collection<PsiFile> twigChild = TwigUtil.getTemplateFileReferences(psiFile, files);
222-
if(twigChild.size() == 0) {
223-
return null;
224-
}
225-
226-
final String blockName = psiElement.getText();
227-
228-
List<PsiElement> blockTargets = new ArrayList<>();
229-
for(PsiFile psiFile1: twigChild) {
230-
231-
blockTargets.addAll(Arrays.asList(PsiTreeUtil.collectElements(psiFile1, psiElement1 ->
232-
TwigHelper.getBlockTagPattern().accepts(psiElement1) && blockName.equals(psiElement1.getText())))
233-
);
234-
235-
}
236-
210+
Collection<PsiElement> blockTargets = TwigHelper.getBlocksByImplementations(psiElement, files);
237211
if(blockTargets.size() == 0) {
238212
return null;
239213
}
@@ -257,7 +231,10 @@ private LineMarkerInfo attachBlockOverwrites(PsiElement psiElement) {
257231

258232
List<GotoRelatedItem> gotoRelatedItems = new ArrayList<>();
259233
for(PsiElement blockTag: blocks) {
260-
gotoRelatedItems.add(new RelatedPopupGotoLineMarker.PopupGotoRelatedItem(blockTag, TwigUtil.getPresentableTemplateName(getTemplateFilesByName(psiElement.getProject()).getTemplates(), blockTag, true)).withIcon(TwigIcons.TwigFileIcon, Symfony2Icons.TWIG_LINE_MARKER));
234+
gotoRelatedItems.add(new RelatedPopupGotoLineMarker.PopupGotoRelatedItem(
235+
blockTag,
236+
TwigUtil.getPresentableTemplateName(getTemplateFilesByName(psiElement.getProject()).getTemplates(), blockTag, true)
237+
).withIcon(TwigIcons.TwigFileIcon, Symfony2Icons.TWIG_LINE_MARKER));
261238
}
262239

263240
// single item has no popup

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ public void testConditionalInvalidGiveBestPossibleStrings() {
8484
assertFalse(twigExtendsValues.contains("base.html"));
8585
}
8686

87+
/**
88+
* @see TwigHelper#getBlocksInFile
89+
*/
8790
public void testBlocksInFileCollector() {
8891
assertEquals("foo", buildBlocks("{% block foo %}").iterator().next().getName());
8992
assertEquals("foo", buildBlocks("{% block \"foo\" %}").iterator().next().getName());
@@ -95,6 +98,9 @@ public void testBlocksInFileCollector() {
9598

9699
assertNotNull(buildBlocks("{%- block 'foo' -%}").iterator().next().getPsiFile());
97100
assertSize(1, buildBlocks("{%- block 'foo' -%}").iterator().next().getBlock());
101+
102+
assertEquals("foobar_block", buildBlocks("{{ block('foobar_block') }}").iterator().next().getName());
103+
assertEquals("foobar_block", buildBlocks("{{ block(\"foobar_block\") }}").iterator().next().getName());
98104
}
99105

100106
/**

0 commit comments

Comments
 (0)