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

Feature/symbol search.restore #2390

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
@@ -0,0 +1,285 @@
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.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.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.templating.util.TwigUtil;
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 <[email protected]>
*/
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 {
@NotNull
final private Project project;

private ContainerCollectionResolver.ServiceCollector serviceCollector;
private Map<String, Set<VirtualFile>> templateMap;
private Map<String, Route> routes;
private Set<String> twigMacroSet;
private Map<String, LookupElement> lookupElements;

private Symfony2NavigationContributor(@NotNull 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, Set<VirtualFile>> getTemplateMap() {
if(this.templateMap == null) {
this.templateMap = TwigUtil.getTemplateMap(this.project, 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 = TwigUtil.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<? super String> processor, @NotNull GlobalSearchScope scope, @Nullable IdFilter filter) {
for(String name: getServiceCollector().getServices().keySet()) {
processor.process(name);
}

for(String templateName: getTemplateMap().keySet()) {
processor.process(templateName);
}

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
for (Map<String, TwigExtension> extensionMap : Arrays.asList(TwigExtensionParser.getFilters(project), TwigExtensionParser.getFunctions(project))) {
for(String twigFilter: extensionMap.keySet()) {
processor.process(twigFilter);
}
}
}

@Override
public void processElementsWithName(@NotNull String name, @NotNull Processor<? super 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"));
}
}

}
}

// @TODO name filter
if(getTemplateMap().containsKey(name)) {
for (PsiFile psiFile : TwigUtil.getTemplatePsiElements(project, name)) {
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.isEmpty()) {
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: TwigUtil.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.getPhpClass(), name, Symfony2Icons.SYMFONY, "Command"));
}
}

// Twig Extensions
for (Map<String, TwigExtension> extensionMap : Arrays.asList(TwigExtensionParser.getFilters(project), TwigExtensionParser.getFunctions(project))) {
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 @NotNull [] getNames(Project project, boolean includeNonProjectItems) {
return new String[0];
}

@NotNull
@Override
public NavigationItem @NotNull [] getItemsByName(String name, String pattern, Project project, boolean includeNonProjectItems) {
return new NavigationItem[0];
}
}

static class MyGotoCallback extends GotoActionCallback<FileType> {
@Override
public void elementChosen(ChooseByNamePopup popup, Object element) {
if(element instanceof NavigationItem) {
((NavigationItem) element).navigate(true);
}
}
}
}

3 changes: 3 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,9 @@
<add-to-group group-id="NewGroup" anchor="before" relative-to-action="SymfonyGroup" />
</group>

<action id="Symfony2Symbols" class="fr.adrienbrault.idea.symfony2plugin.action.SymfonySymbolSearchAction" text="Symfony Symbol..." icon="SymfonyIcons.Symfony">
<add-to-group group-id="GoToTargetEx"/>
</action>
<action icon="SymfonyIcons.TwigBlockOverwrite"
id="SymfonyTwigBlockOverwrite"
class="fr.adrienbrault.idea.symfony2plugin.templating.action.TwigBlockOverwriteGenerator"
Expand Down