package fr.adrienbrault.idea.symfony2plugin.templating.dict; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.intellij.psi.PsiRecursiveElementWalkingVisitor; import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.twig.TwigFile; import com.jetbrains.twig.elements.TwigCompositeElement; import com.jetbrains.twig.elements.TwigElementTypes; import com.jetbrains.twig.elements.TwigExtendsTag; import fr.adrienbrault.idea.symfony2plugin.TwigHelper; import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil; import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; /** * @author Daniel Espendiller <daniel@espendiller.net> */ public class TwigBlockParser { private boolean withSelfBlock = false; private Map<String, VirtualFile> twigFilesByName; public TwigBlockParser(Map<String, VirtualFile> twigFilesByName) { this.twigFilesByName = twigFilesByName; } public List<TwigBlock> visit(@NotNull PsiFile[] file) { List<TwigBlock> blocks = new ArrayList<>(); for (PsiFile psiFile : file) { blocks.addAll(this.walk(psiFile, psiFile.getName(), new ArrayList<>(), 0)); } return blocks; } public List<TwigBlock> walk(@Nullable PsiFile file) { return this.walk(file, "self", new ArrayList<>(), 0); } public List<TwigBlock> walk(@Nullable PsiFile file, String shortcutName, List<TwigBlock> current, int depth) { if(file == null) { return current; } // dont match on self file !? if(depth > 0 || (withSelfBlock && depth == 0)) { if(file instanceof TwigFile) { Collection<TwigBlock> blocksInFile = TwigHelper.getBlocksInFile((TwigFile) file); // @TODO: remove this here just presentation for (TwigBlock twigBlock : blocksInFile) { twigBlock.setShortcutName(shortcutName); } current.addAll(blocksInFile); } } // limit recursive calls if(depth++ > 20) { return current; } final Map<VirtualFile, String> virtualFiles = new HashMap<>(); // {% extends 'foo' %} // find extend in self for(TwigExtendsTag extendsTag : PsiTreeUtil.getChildrenOfTypeAsList(file, TwigExtendsTag.class)) { for (String s : TwigHelper.getTwigExtendsTagTemplates(extendsTag)) { String templateName = TwigHelper.normalizeTemplateName(s); if(twigFilesByName.containsKey(templateName)) { virtualFiles.put(twigFilesByName.get(templateName), templateName); } } } // {% use 'foo' %} for(TwigCompositeElement twigCompositeElement: PsiTreeUtil.getChildrenOfTypeAsList(file, TwigCompositeElement.class)) { if(twigCompositeElement.getNode().getElementType() == TwigElementTypes.TAG) { twigCompositeElement.acceptChildren(new PsiRecursiveElementWalkingVisitor() { @Override public void visitElement(PsiElement element) { if(TwigHelper.getTwigTagUseNamePattern().accepts(element)) { String templateName = PsiElementUtils.trimQuote(element.getText()); if(StringUtils.isNotBlank(templateName)) { String templateNameNormalized = TwigHelper.normalizeTemplateName(TwigHelper.normalizeTemplateName(templateName)); if(twigFilesByName.containsKey(templateNameNormalized)) { virtualFiles.put(twigFilesByName.get(templateName), templateNameNormalized); } } } super.visitElement(element); } }); } } for(Map.Entry<VirtualFile, String> entry : virtualFiles.entrySet()) { // can be null if deleted during iteration VirtualFile key = entry.getKey(); if(key == null) { continue; } PsiFile psiFile = PsiManager.getInstance(file.getProject()).findFile(key); if(psiFile instanceof TwigFile) { this.walk(psiFile, TwigUtil.getFoldingTemplateNameOrCurrent(entry.getValue()), current, depth); } } return current; } public TwigBlockParser withSelfBlocks(boolean withSelfBlock) { this.withSelfBlock = withSelfBlock; return this; } }