Skip to content

Commit

Permalink
Merge pull request #2241 from Haehnchen/feature/embed-twig-impl
Browse files Browse the repository at this point in the history
add implements Twig block target for embed blocks
  • Loading branch information
Haehnchen authored Oct 14, 2023
2 parents 04fb1ff + 7be3bbf commit 67a2fff
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ public DataIndexer<String, Set<String>, FileContent> getIndexer() {

PsiFile psiFile = fileContent.getPsiFile();
if (psiFile instanceof TwigFile twigFile) {
TwigUtil.visitEmbedBlocks(twigFile, pair -> {
String templateName = pair.getFirst();
TwigUtil.visitEmbedBlocks(twigFile, embedBlock -> {
String templateName = embedBlock.templateName();

blocks.putIfAbsent(templateName, new HashSet<>());
blocks.get(templateName).add(pair.getSecond());
blocks.get(templateName).add(embedBlock.blockName());
});
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package fr.adrienbrault.idea.symfony2plugin.templating.dict;

import com.jetbrains.twig.elements.TwigBlockStatement;
import org.jetbrains.annotations.NotNull;

/**
* @author Daniel Espendiller <[email protected]>
*/
public record TwigBlockEmbed(@NotNull String templateName, @NotNull String blockName, @NotNull TwigBlockStatement target) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@
import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment;
import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag;
import com.jetbrains.php.lang.psi.PhpPsiUtil;
import com.jetbrains.php.lang.psi.elements.*;
import com.jetbrains.php.lang.psi.elements.Function;
import com.jetbrains.php.lang.psi.elements.Method;
import com.jetbrains.php.lang.psi.elements.PhpClass;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import com.jetbrains.php.phpunit.PhpUnitUtil;
import com.jetbrains.twig.TwigFile;
import com.jetbrains.twig.TwigFileType;
Expand Down Expand Up @@ -1784,8 +1787,7 @@ public void visitElement(PsiElement element) {
return block;
}

@NotNull
public static void visitEmbedBlocks(@NotNull TwigFile psiFile, @NotNull Consumer<Pair<String, String>> consumer) {
public static void visitEmbedBlocks(@NotNull TwigFile psiFile, @NotNull Consumer<TwigBlockEmbed> consumer) {
PsiElement[] embedStatements = PsiTreeUtil.collectElements(psiFile, psiElement ->
psiElement instanceof TwigCompositeElement && psiElement.getNode().getElementType() == TwigElementTypes.EMBED_STATEMENT
);
Expand All @@ -1805,7 +1807,7 @@ public static void visitEmbedBlocks(@NotNull TwigFile psiFile, @NotNull Consumer
String blockName = twigBlockStatement.getName();

if (blockName != null && !blockName.isBlank()) {
consumer.consume(Pair.create(templateNameForEmbedTag, blockName));
consumer.consume(new TwigBlockEmbed(templateNameForEmbedTag, blockName, twigBlockStatement));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,22 @@
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.indexing.FileBasedIndex;
import com.jetbrains.twig.TwigFile;
import com.jetbrains.twig.TwigTokenTypes;
import com.jetbrains.twig.elements.TwigBlockStatement;
import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.TwigBlockEmbedIndex;
import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.TwigBlockIndexExtension;
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigBlock;
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
import fr.adrienbrault.idea.symfony2plugin.twig.loader.FileImplementsLazyLoader;
import fr.adrienbrault.idea.symfony2plugin.twig.loader.FileOverwritesLazyLoader;
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;

Expand Down Expand Up @@ -54,25 +59,41 @@ private static Collection<TwigBlock> collectBlocksInFile(boolean includeSelf, @N
*/
public static boolean hasBlockImplementations(@NotNull PsiElement blockPsiName, @NotNull FileImplementsLazyLoader implementsLazyLoader) {
String blockName = blockPsiName.getText();
if(StringUtils.isBlank(blockName)) {
if (StringUtils.isBlank(blockName)) {
return false;
}

PsiFile psiFile = blockPsiName.getContainingFile();
if(psiFile == null) {
if (psiFile == null) {
return false;
}

Collection<VirtualFile> twigChild = implementsLazyLoader.getFiles();
if(twigChild.size() == 0) {

Set<VirtualFile> virtualFiles = new HashSet<>(twigChild);
VirtualFile virtualFile = psiFile.getVirtualFile();
if (virtualFile != null) {
virtualFiles.add(virtualFile);
}

Project project = psiFile.getProject();

for (VirtualFile file : virtualFiles) {
if (hasBlockImplementationsForEmbed(project, file, blockName)) {
return true;
}
}

if (twigChild.isEmpty()) {
return false;
}

return hasBlockNamesForFiles(blockPsiName.getProject(), blockName, twigChild);
return hasBlockNamesForFiles(project, blockName, twigChild);
}

/**
* {% extends 'foobar.html.twig' %}
* {% embed 'foobar.html.twig' %}
*
* {{ block('foo<caret>bar') }}
* {% block 'foo<caret>bar' %}
Expand All @@ -81,29 +102,51 @@ public static boolean hasBlockImplementations(@NotNull PsiElement blockPsiName,
@NotNull
public static Collection<PsiElement> getBlockImplementationTargets(@NotNull PsiElement blockPsiName) {
String blockName = blockPsiName.getText();
if(StringUtils.isBlank(blockName)) {
if (StringUtils.isBlank(blockName)) {
return Collections.emptyList();
}

PsiFile psiFile = blockPsiName.getContainingFile();
if(psiFile == null) {
if (psiFile == null) {
return Collections.emptyList();
}

Collection<VirtualFile> twigChild = TwigUtil.getTemplatesExtendingFile(psiFile.getProject(), psiFile.getVirtualFile());
if(twigChild.size() == 0) {
return Collections.emptyList();
}
Project project = psiFile.getProject();
VirtualFile currentVirtualFile = psiFile.getVirtualFile();
Collection<VirtualFile> parentTwigExtendingFiles = TwigUtil.getTemplatesExtendingFile(project, currentVirtualFile);

HashSet<VirtualFile> embedFiles = new HashSet<>(parentTwigExtendingFiles);
embedFiles.add(currentVirtualFile);

Collection<PsiElement> blockTargets = new HashSet<>();

Collection<PsiElement> blockTargets = new ArrayList<>();
// embed targets
for (VirtualFile vFile : embedFiles) {
Set<String> templateNames = TwigUtil.getTemplateNamesForFile(blockPsiName.getProject(), vFile).stream()
.map(TwigUtil::normalizeTemplateName)
.collect(Collectors.toSet());

for (VirtualFile virtualFile : twigChild) {
PsiFile file = PsiManager.getInstance(blockPsiName.getProject()).findFile(virtualFile);
if(!(file instanceof TwigFile)) {
for (String templateName : templateNames) {
for (VirtualFile containingFile : FileBasedIndex.getInstance().getContainingFiles(TwigBlockEmbedIndex.KEY, templateName, GlobalSearchScope.allScope(project))) {
if(!(PsiManager.getInstance(project).findFile(containingFile) instanceof TwigFile twigFile)) {
continue;
}

TwigUtil.visitEmbedBlocks(twigFile, embedBlock -> {
if (embedBlock.blockName().equals(blockName) && templateName.equals(embedBlock.templateName())) {
blockTargets.add(getBlockNamePsiElementTarget(embedBlock.target()));
}
});
}
}
}

for (VirtualFile parentTwigExtendingFile : parentTwigExtendingFiles) {
if (!(PsiManager.getInstance(project).findFile(parentTwigExtendingFile) instanceof TwigFile twigFile)) {
continue;
}

for (TwigBlock twigBlock : TwigUtil.getBlocksInFile((TwigFile) file)) {
for (TwigBlock twigBlock : TwigUtil.getBlocksInFile(twigFile)) {
if (blockName.equals(twigBlock.getName())) {
blockTargets.add(twigBlock.getTarget());
}
Expand All @@ -113,6 +156,22 @@ public static Collection<PsiElement> getBlockImplementationTargets(@NotNull PsiE
return blockTargets;
}

/**
* Extracted named block element for ui presentable
*/
private static PsiElement getBlockNamePsiElementTarget(@NotNull TwigBlockStatement twigBlockStatement) {
PsiElement blockTag = twigBlockStatement.getFirstChild();

if (blockTag != null) {
PsiElement childrenOfType = PsiElementUtils.getChildrenOfType(blockTag, PlatformPatterns.psiElement(TwigTokenTypes.IDENTIFIER));
if (childrenOfType != null) {
return childrenOfType;
}
}

return twigBlockStatement;
}

/**
* Collect every block name by given file name; resolve the "extends" or "embed" scope
*/
Expand Down Expand Up @@ -140,7 +199,7 @@ public static Collection<PsiElement> getBlockOverwriteTargets(@NotNull PsiElemen
public static boolean hasBlockOverwrites(@NotNull PsiElement psiElement, @NotNull FileOverwritesLazyLoader fileOverwritesLazyLoader) {
String blockName = psiElement.getText();

if(StringUtils.isBlank(blockName)) {
if (StringUtils.isBlank(blockName)) {
return false;
}

Expand All @@ -154,6 +213,21 @@ public static boolean hasBlockOverwrites(@NotNull PsiElement psiElement, @NotNul
return hasBlockNamesForFiles(psiElement.getProject(), blockName, virtualFiles);
}

private static boolean hasBlockImplementationsForEmbed(@NotNull Project project, @NotNull VirtualFile virtualFile, @NotNull String blockName) {
for (String templateName : TwigUtil.getTemplateNamesForFile(project, virtualFile)) {
Set<String> blockNames = FileBasedIndex.getInstance().getValues(TwigBlockEmbedIndex.KEY, templateName, GlobalSearchScope.allScope(project))
.stream()
.flatMap(Set::stream)
.collect(Collectors.toSet());

if (blockNames.contains(blockName)) {
return true;
}
}

return false;
}

/**
* Collect every block name by given file name; resolve the "extends"
*/
Expand Down

0 comments on commit 67a2fff

Please sign in to comment.