Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add implements Twig block target for embed blocks #2241

Merged
merged 1 commit into from
Oct 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading