package fr.adrienbrault.idea.symfony2plugin.action; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.ide.actions.GotoActionBase; import com.intellij.ide.util.gotoByName.ChooseByNamePopup; import com.intellij.navigation.ChooseByNameContributor; import com.intellij.navigation.ChooseByNameContributorEx; import com.intellij.navigation.NavigationItem; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; 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.search.GlobalSearchScope; import com.intellij.util.Processor; import com.intellij.util.indexing.FindSymbolParameters; import com.intellij.util.indexing.IdFilter; import com.jetbrains.php.lang.psi.elements.PhpClass; import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons; import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent; import fr.adrienbrault.idea.symfony2plugin.TwigHelper; import fr.adrienbrault.idea.symfony2plugin.action.model.SymfonySymbolSearchModel; import fr.adrienbrault.idea.symfony2plugin.dic.ContainerService; import fr.adrienbrault.idea.symfony2plugin.doctrine.EntityHelper; import fr.adrienbrault.idea.symfony2plugin.doctrine.EntityReference; import fr.adrienbrault.idea.symfony2plugin.navigation.NavigationItemEx; import fr.adrienbrault.idea.symfony2plugin.routing.Route; import fr.adrienbrault.idea.symfony2plugin.routing.RouteHelper; import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver; import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigExtension; import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigExtensionParser; import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil; import fr.adrienbrault.idea.symfony2plugin.util.SymfonyCommandUtil; import fr.adrienbrault.idea.symfony2plugin.util.dict.SymfonyCommand; import icons.TwigIcons; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.*; /** * @author Daniel Espendiller <daniel@espendiller.net> */ public class SymfonySymbolSearchAction extends GotoActionBase { @Override protected void gotoActionPerformed(AnActionEvent paramAnActionEvent) { FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.popup.file"); Project localProject = paramAnActionEvent.getData(CommonDataKeys.PROJECT); if (localProject != null) { SymfonySymbolSearchModel searchModel = new SymfonySymbolSearchModel(localProject, new ChooseByNameContributor[] { new Symfony2NavigationContributor(localProject) }); showNavigationPopup(paramAnActionEvent, searchModel, new MyGotoCallback(), null, true); } } @Override public void update(AnActionEvent event) { super.update(event); Project project = event.getData(CommonDataKeys.PROJECT); boolean enabled = Symfony2ProjectComponent.isEnabled(project); event.getPresentation().setVisible(enabled); event.getPresentation().setEnabled(enabled); } private static class Symfony2NavigationContributor implements ChooseByNameContributorEx, DumbAware { final private Project project; private ContainerCollectionResolver.ServiceCollector serviceCollector; private Map<String, VirtualFile> templateMap; private Map<String, Route> routes; private Set<String> twigMacroSet; private Map<String, LookupElement> lookupElements; public Symfony2NavigationContributor(Project project) { this.project = project; } private ContainerCollectionResolver.ServiceCollector getServiceCollector() { if(this.serviceCollector == null) { this.serviceCollector = ContainerCollectionResolver.ServiceCollector.create(this.project); } return this.serviceCollector; } private Map<String, VirtualFile> getTemplateMap() { if(this.templateMap == null) { this.templateMap = TwigHelper.getTemplateFilesByName(this.project, true, true); } return this.templateMap; } private Map<String, Route> getRoutes() { if(this.routes == null) { this.routes = RouteHelper.getAllRoutes(project); } return this.routes; } private Set<String> getTwigMacroSet() { if(this.twigMacroSet == null) { this.twigMacroSet = TwigHelper.getTwigMacroSet(this.project); } return this.twigMacroSet; } private Map<String, LookupElement> getModelLookupElements() { if(this.lookupElements == null) { List<LookupElement> modelLookupElements = EntityReference.getModelLookupElements(this.project); this.lookupElements = new HashMap<>(); for(LookupElement lookupElement: modelLookupElements) { this.lookupElements.put(lookupElement.getLookupString(), lookupElement); } } return this.lookupElements; } @Override public void processNames(@NotNull Processor<String> processor, @NotNull GlobalSearchScope scope, @Nullable IdFilter filter) { for(String name: getServiceCollector().getServices().keySet()) { processor.process(name); } for(Map.Entry<String, VirtualFile> entry: getTemplateMap().entrySet()) { processor.process(entry.getKey()); } for(String name: getRoutes().keySet()) { processor.process(name); } for(Map.Entry<String, Route> entry: getRoutes().entrySet()) { processor.process(entry.getKey()); String path = entry.getValue().getPath(); if(path != null) { processor.process(path); } } for(String name: getTwigMacroSet()) { processor.process(name); } for(String name: getModelLookupElements().keySet()) { processor.process(name); } for(SymfonyCommand command: SymfonyCommandUtil.getCommands(project)) { processor.process(command.getName()); } // Twig Extensions TwigExtensionParser twigExtensionParser = new TwigExtensionParser(project); for (Map<String, TwigExtension> extensionMap : Arrays.asList(twigExtensionParser.getFilters(), twigExtensionParser.getFunctions())) { for(String twigFilter: extensionMap.keySet()) { processor.process(twigFilter); } } } @Override public void processElementsWithName(@NotNull String name, @NotNull Processor<NavigationItem> processor, @NotNull FindSymbolParameters parameters) { for(ContainerService containerService: getServiceCollector().collect()) { if(containerService.getName().equals(name)) { String serviceClass = getServiceCollector().resolve(name); if (serviceClass != null) { PhpClass phpClass = PhpElementsUtil.getClassInterface(this.project, serviceClass); if(phpClass != null) { processor.process(new NavigationItemEx(phpClass, containerService.getName(), containerService.isWeak() ? Symfony2Icons.SERVICE_PRIVATE_OPACITY : Symfony2Icons.SERVICE, "Service")); } } } } if(getTemplateMap().containsKey(name)) { VirtualFile virtualFile = getTemplateMap().get(name); PsiFile psiFile = PsiManager.getInstance(this.project).findFile(virtualFile); if(psiFile != null) { processor.process(new NavigationItemEx(psiFile, name, psiFile.getFileType().getIcon(), "Template")); } } Set<String> controllers = new HashSet<>(); if(getRoutes().containsKey(name)) { String controllerName = getRoutes().get(name).getController(); if(controllerName != null) { controllers.add(controllerName); } } // route path: /foo/bar for (Route route : getRoutes().values()) { if(!name.equals(route.getPath())) { continue; } String controller = route.getController(); if(controller != null) { controllers.add(controller); } } if(controllers.size() > 0) { for (String controller : controllers) { for(PsiElement psiElement: RouteHelper.getMethodsOnControllerShortcut(this.project, controller)) { processor.process(new NavigationItemEx(psiElement, name, Symfony2Icons.ROUTE, "Route")); } } } if(getTwigMacroSet().contains(name)) { for(PsiElement macroTarget: TwigHelper.getTwigMacroTargets(project, name)) { processor.process(new NavigationItemEx(macroTarget, name, TwigIcons.TwigFileIcon, "Macro")); } } if(getModelLookupElements().containsKey(name)) { PsiElement[] psiElements = EntityHelper.getModelPsiTargets(this.project, name); getModelLookupElements().get(name).getLookupString(); for(PsiElement target: psiElements) { processor.process(new NavigationItemEx(target, name, target.getIcon(0), "Entity")); } } for (SymfonyCommand symfonyCommand : SymfonyCommandUtil.getCommands(project)) { if(symfonyCommand.getName().equals(name)) { processor.process(new NavigationItemEx(symfonyCommand.getPsiElement(), name, Symfony2Icons.SYMFONY, "Command")); } } // Twig Extensions TwigExtensionParser twigExtensionParser = new TwigExtensionParser(project); for (Map<String, TwigExtension> extensionMap : Arrays.asList(twigExtensionParser.getFilters(), twigExtensionParser.getFunctions())) { for(Map.Entry<String, TwigExtension> twigFunc: extensionMap.entrySet()) { if(twigFunc.getKey().equals(name)) { TwigExtension twigExtension = twigFunc.getValue(); PsiElement extensionTarget = TwigExtensionParser.getExtensionTarget(project, twigExtension); if(extensionTarget != null) { processor.process(new NavigationItemEx(extensionTarget, name, TwigExtensionParser.getIcon(twigExtension.getTwigExtensionType()), twigExtension.getTwigExtensionType().toString())); } } } } } @NotNull @Override public String[] getNames(Project project, boolean includeNonProjectItems) { return new String[0]; } @NotNull @Override public NavigationItem[] getItemsByName(String name, String pattern, Project project, boolean includeNonProjectItems) { return new NavigationItem[0]; } } class MyGotoCallback extends GotoActionBase.GotoActionCallback<FileType> { @Override public void elementChosen(ChooseByNamePopup popup, Object element) { if(element instanceof NavigationItem) { ((NavigationItem) element).navigate(true); } } } }