Package fr.adrienbrault.idea.symfony2plugin.templating

Source Code of fr.adrienbrault.idea.symfony2plugin.templating.TwigTemplateGoToLocalDeclarationHandler

package fr.adrienbrault.idea.symfony2plugin.templating;

import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.editor.Editor;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.php.PhpIndex;
import com.jetbrains.php.lang.psi.elements.Field;
import com.jetbrains.php.lang.psi.elements.Method;
import com.jetbrains.php.lang.psi.elements.PhpClass;
import com.jetbrains.twig.TwigLanguage;
import com.jetbrains.twig.TwigTokenTypes;
import com.jetbrains.twig.elements.TwigBlockTag;
import com.jetbrains.twig.elements.TwigElementTypes;
import com.jetbrains.twig.elements.TwigTagWithFileReference;
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
import fr.adrienbrault.idea.symfony2plugin.TwigHelper;
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigExtension;
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigMacro;
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigSet;
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigExtensionParser;
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigTypeResolveUtil;
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
import fr.adrienbrault.idea.symfony2plugin.templating.variable.TwigTypeContainer;
import fr.adrienbrault.idea.symfony2plugin.templating.variable.collector.ControllerDocVariableCollector;
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
import fr.adrienbrault.idea.symfony2plugin.util.RegexPsiElementFilter;
import fr.adrienbrault.idea.symfony2plugin.util.controller.ControllerIndex;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TwigTemplateGoToLocalDeclarationHandler implements GotoDeclarationHandler {

    @Nullable
    @Override
    public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int i, Editor editor) {

        ArrayList<PsiElement> psiElements = new ArrayList<PsiElement>();

        if(!Symfony2ProjectComponent.isEnabled(psiElement)) {
            return null;
        }

        // {{ goto_me() }}
        if (TwigHelper.getPrintBlockFunctionPattern().accepts(psiElement)) {
            psiElements.addAll(Arrays.asList(this.getMacros(psiElement)));
        }

        // {% from 'boo.html.twig' import goto_me %}
        if (TwigHelper.getTemplateImportFileReferenceTagPattern().accepts(psiElement)) {
            psiElements.addAll(Arrays.asList(this.getMacros(psiElement)));
        }

        // {% set foo  %}
        // {% set foo = bar %}
        if (PlatformPatterns
            .psiElement(TwigTokenTypes.IDENTIFIER)
            .withParent(
                PlatformPatterns.psiElement(TwigElementTypes.PRINT_BLOCK)
            ).withLanguage(TwigLanguage.INSTANCE).accepts(psiElement)) {

            psiElements.addAll(Arrays.asList(this.getSets(psiElement)));
        }

        // {{ function( }}
        if (PlatformPatterns
            .psiElement(TwigTokenTypes.IDENTIFIER)
            .beforeLeaf(PlatformPatterns.psiElement(TwigTokenTypes.LBRACE))
            .withParent(
                PlatformPatterns.psiElement(TwigElementTypes.PRINT_BLOCK)
            ).withLanguage(TwigLanguage.INSTANCE).accepts(psiElement)) {

            psiElements.addAll(Arrays.asList(this.getFunctions(psiElement)));
        }

        if(TwigHelper.getTypeCompletionPattern().accepts(psiElement)
            || TwigHelper.getPrintBlockFunctionPattern().accepts(psiElement)
            || TwigHelper.getVariableTypePattern().accepts(psiElement))
        {
            psiElements.addAll(Arrays.asList(this.getTypeGoto(psiElement)));
        }

        if(TwigHelper.getTwigDocBlockMatchPattern(ControllerDocVariableCollector.DOC_PATTERN).accepts(psiElement)) {
            psiElements.addAll(Arrays.asList(this.getControllerNameGoto(psiElement)));
        }

        // {{ parent() }}
        if(TwigHelper.getParentFunctionPattern().accepts(psiElement)) {
            psiElements.addAll(Arrays.asList(this.getParentGoto(psiElement)));
        }

        // constant('Post::PUBLISHED')
        if(TwigHelper.getPrintBlockFunctionPattern("constant").accepts(psiElement)) {
            psiElements.addAll(this.getConstantGoto(psiElement));
        }

        return psiElements.toArray(new PsiElement[psiElements.size()]);
    }

    private Collection<PsiElement> getConstantGoto(PsiElement psiElement) {

        Collection<PsiElement> targetPsiElements = new ArrayList<PsiElement>();

        String contents = psiElement.getText();
        if(StringUtils.isBlank(contents)) {
            return targetPsiElements;
        }

        // global constant
        if(!contents.contains(":")) {
            targetPsiElements.addAll(PhpIndex.getInstance(psiElement.getProject()).getConstantsByName(contents));
            return targetPsiElements;
        }

        // resolve class constants
        String[] parts = contents.split("::");
        if(parts.length != 2) {
            return targetPsiElements;
        }

        PhpClass phpClass = PhpElementsUtil.getClassInterface(psiElement.getProject(), parts[0]);
        if(phpClass == null) {
            return targetPsiElements;
        }

        Field field = phpClass.findFieldByName(parts[1], true);
        if(field != null) {
            targetPsiElements.add(field);
        }

        return targetPsiElements;
    }

    private PsiElement[] getTypeGoto(PsiElement psiElement) {

        ArrayList<PsiElement> targetPsiElements = new ArrayList<PsiElement>();

        // class, class.method, class.method.method
        // click on first item is our class name
        String[] beforeLeaf = TwigTypeResolveUtil.formatPsiTypeName(psiElement);
        if(beforeLeaf.length == 0) {
            Collection<TwigTypeContainer> twigTypeContainers = TwigTypeResolveUtil.resolveTwigMethodName(psiElement, TwigTypeResolveUtil.formatPsiTypeName(psiElement, true));
            for(TwigTypeContainer twigTypeContainer: twigTypeContainers) {
                if(twigTypeContainer.getPhpNamedElement() != null) {
                    targetPsiElements.add(twigTypeContainer.getPhpNamedElement());
                }
            }

        } else {
            Collection<TwigTypeContainer> types = TwigTypeResolveUtil.resolveTwigMethodName(psiElement, beforeLeaf);
            // provide method / field goto
            for(TwigTypeContainer twigTypeContainer: types) {
                if(twigTypeContainer.getPhpNamedElement() != null) {
                    targetPsiElements.addAll(TwigTypeResolveUtil.getTwigPhpNameTargets(twigTypeContainer.getPhpNamedElement(), psiElement.getText()));
                }
            }
        }

        return targetPsiElements.toArray(new PsiElement[targetPsiElements.size()]);
    }

    private PsiElement[] getFunctions(PsiElement psiElement) {
        HashMap<String, TwigExtension> functions = new TwigExtensionParser(psiElement.getProject()).getFunctions();

        String funcName = psiElement.getText();
        if(!functions.containsKey(funcName)) {
            return new PsiElement[0];
        }

        return PhpElementsUtil.getPsiElementsBySignature(psiElement.getProject(), functions.get(funcName).getSignature());
     }

    private PsiElement[] getSets(PsiElement psiElement) {
        String funcName = psiElement.getText();
        for(TwigSet twigSet: TwigUtil.getSetDeclaration(psiElement.getContainingFile())) {
           if(twigSet.getName().equals(funcName)) {
               return PsiTreeUtil.collectElements(psiElement.getContainingFile(), new RegexPsiElementFilter(
                   TwigTagWithFileReference.class,
                   "\\{%\\s?set\\s?" + Pattern.quote(funcName) + "\\s?.*")
               );
           }
        }

        return new PsiElement[0];
    }

    private PsiElement[] getMacros(PsiElement psiElement) {
        String funcName = psiElement.getText();
        String funcNameSearch = funcName;

        ArrayList<TwigMacro> twigMacros;

        // check for complete file as namespace import {% import "file" as foo %}
        if(psiElement.getPrevSibling() != null && PlatformPatterns.psiElement(TwigTokenTypes.DOT).accepts(psiElement.getPrevSibling())) {
            PsiElement psiElement1 = psiElement.getPrevSibling().getPrevSibling();
            if(psiElement1 == null) {
                return null;
            }

            funcNameSearch = psiElement1.getText() + "." + funcName;
            twigMacros = TwigUtil.getImportedMacrosNamespaces(psiElement.getContainingFile());
        } else {
            twigMacros = TwigUtil.getImportedMacros(psiElement.getContainingFile());
        }

        for(TwigMacro twigMacro : twigMacros) {
            if(twigMacro.getName().equals(funcNameSearch)) {

                // switch to alias mode
                final String macroName = twigMacro.getOriginalName() == null ? funcName : twigMacro.getOriginalName();

                PsiFile psiFile = TwigHelper.getTemplateFileByName(psiElement.getProject(), twigMacro.getTemplate());

                if(psiFile != null) {
                    return PsiTreeUtil.collectElements(psiFile, new RegexPsiElementFilter(
                        TwigElementTypes.MACRO_TAG,
                        "\\{%\\s?macro\\s?" + Pattern.quote(macroName) + "\\s?\\(.*%}")
                    );
                }

            }
        }

        return new PsiElement[0];
    }


    private PsiElement[] getControllerNameGoto(PsiElement psiElement) {
        Pattern pattern = Pattern.compile(ControllerDocVariableCollector.DOC_PATTERN);
        Matcher matcher = pattern.matcher(psiElement.getText());
        if (!matcher.find()) {
            return new PsiElement[0];
        }

        String controllerName = matcher.group(1);

        Method method = ControllerIndex.getControllerMethod(psiElement.getProject(), controllerName);
        if(method == null) {
            return new PsiElement[0];
        }

        return new PsiElement[] { method };
    }

    private PsiElement[] getParentGoto(PsiElement psiElement) {

        // find printblock
        PsiElement printBlock = psiElement.getParent();
        if(printBlock == null || !PlatformPatterns.psiElement(TwigElementTypes.PRINT_BLOCK).accepts(printBlock)) {
            return new PsiElement[0];
        }

        // printblock need to be child block statement
        PsiElement blockStatement = printBlock.getParent();
        if(blockStatement == null || !PlatformPatterns.psiElement(TwigElementTypes.BLOCK_STATEMENT).accepts(blockStatement)) {
            return new PsiElement[0];
        }

        // BlockTag is first child of block statement
        PsiElement blockTag = blockStatement.getFirstChild();
        if(!(blockTag instanceof TwigBlockTag)) {
            return new PsiElement[0];
        }

        String blockName = ((TwigBlockTag) blockTag).getName();
        return TwigTemplateGoToDeclarationHandler.getBlockNameGoTo(psiElement.getContainingFile(), blockName);
    }

    @Nullable
    @Override
    public String getActionText(DataContext dataContext) {
        return null;
    }
}
TOP

Related Classes of fr.adrienbrault.idea.symfony2plugin.templating.TwigTemplateGoToLocalDeclarationHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.