diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/javascript/JavascriptCompletionNavigationContributor.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/javascript/JavascriptCompletionNavigationContributor.java index 0f4d4bb50..ef61e2b0a 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/javascript/JavascriptCompletionNavigationContributor.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/javascript/JavascriptCompletionNavigationContributor.java @@ -13,7 +13,6 @@ import com.intellij.openapi.editor.Editor; import com.intellij.patterns.InitialPatternCondition; import com.intellij.patterns.PlatformPatterns; -import com.intellij.patterns.PsiElementPattern; import com.intellij.psi.PsiElement; import com.intellij.util.ProcessingContext; import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons; @@ -84,14 +83,6 @@ public static class GotoDeclaration implements GotoDeclarationHandler { } } - private static PsiElementPattern.@NotNull Capture getPlace() { - return PlatformPatterns.psiElement(JSLiteralExpression.class).withParent( - PlatformPatterns.psiElement(JSArgumentList.class).withParent( - PlatformPatterns.psiElement(JSCallExpression.class) - ) - ); - } - /** * insertHandler are after already inserted string, so remove it and replace it :) */ diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/navigation/NavigationItemExStateless.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/navigation/NavigationItemExStateless.java index 29efd7418..938745234 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/navigation/NavigationItemExStateless.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/navigation/NavigationItemExStateless.java @@ -89,16 +89,16 @@ public Icon getIcon(boolean b) { public static NavigationItemExStateless create(@NotNull PsiElement psiElement, @NotNull String name, @NotNull Icon icon, @NotNull String locationString, boolean appendBundleLocation) { String locationPathString = locationString; - if(appendBundleLocation) { + if (appendBundleLocation) { PsiFile psiFile = psiElement.getContainingFile(); - if(psiFile != null) { + if (psiFile != null) { locationPathString = locationString + " " + psiFile.getName(); String bundleName = psiFile.getVirtualFile().getPath(); - if(bundleName.contains("Bundle")) { + if (bundleName.contains("Bundle")) { bundleName = bundleName.substring(0, bundleName.lastIndexOf("Bundle")); - if(bundleName.length() > 1 && bundleName.contains("/")) { + if (bundleName.length() > 1 && bundleName.contains("/")) { locationPathString = locationPathString + " " + bundleName.substring(bundleName.lastIndexOf("/") + 1) + "::" + psiFile.getName(); } } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/navigation/NavigationItemPresentableOverwrite.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/navigation/NavigationItemPresentableOverwrite.java new file mode 100644 index 000000000..488035bb2 --- /dev/null +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/navigation/NavigationItemPresentableOverwrite.java @@ -0,0 +1,134 @@ +package fr.adrienbrault.idea.symfony2plugin.navigation; + +import com.intellij.ide.util.PsiNavigationSupport; +import com.intellij.navigation.ItemPresentation; +import com.intellij.navigation.NavigationItem; +import com.intellij.openapi.util.NlsSafe; +import com.intellij.pom.Navigatable; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +/** + * @author Daniel Espendiller + */ +public class NavigationItemPresentableOverwrite implements NavigationItem, ItemPresentation { + + @NotNull + private final PsiElement psiElement; + + @NotNull + private final String presentableText; + + @NotNull + private final Icon icon; + + @NotNull + private final String locationString; + private final String name; + + private NavigationItemPresentableOverwrite(@NotNull PsiElement psiElement, @NotNull String presentableText, @NotNull Icon icon, @NotNull String locationString, @NotNull String name) { + this.psiElement = psiElement; + this.presentableText = presentableText; + this.name = name; + this.icon = icon; + this.locationString = locationString; + } + + @Override + public @NotNull String getName() { + return this.name; + } + + @Nullable + @Override + public ItemPresentation getPresentation() { + return new ItemPresentation() { + @Override + public @NlsSafe @NotNull String getPresentableText() { + return NavigationItemPresentableOverwrite.this.presentableText; + } + + @Override + public @NlsSafe @NotNull String getLocationString() { + return NavigationItemPresentableOverwrite.this.locationString; + } + + @Override + public @Nullable Icon getIcon(boolean unused) { + return NavigationItemPresentableOverwrite.this.getIcon(unused); + } + }; + } + + @Override + public void navigate(boolean requestFocus) { + final Navigatable descriptor = PsiNavigationSupport.getInstance().getDescriptor(this.psiElement); + if (descriptor != null) { + descriptor.navigate(requestFocus); + } + } + + @Override + public boolean canNavigate() { + return PsiNavigationSupport.getInstance().canNavigate(this.psiElement); + } + + @Override + public boolean canNavigateToSource() { + return canNavigate(); + } + + @Override + public String toString() { + return this.presentableText; + } + + @Override + public @NotNull String getPresentableText() { + return presentableText; + } + + @Override + public @NotNull String getLocationString() { + return this.locationString; + } + + @Nullable + @Override + public Icon getIcon(boolean b) { + return icon; + } + + public static NavigationItemPresentableOverwrite create(@NotNull PsiElement psiElement, @NotNull String presentableText, @NotNull Icon icon, @NotNull String locationString, boolean appendBundleLocation, @NotNull String name) { + String locationPathString = locationString; + + if(appendBundleLocation) { + PsiFile psiFile = psiElement.getContainingFile(); + if(psiFile != null) { + locationPathString = locationString + " " + psiFile.getName(); + + String bundleName = psiFile.getVirtualFile().getPath(); + + if(bundleName.contains("Bundle")) { + bundleName = bundleName.substring(0, bundleName.lastIndexOf("Bundle")); + if(bundleName.length() > 1 && bundleName.contains("/")) { + locationPathString = locationPathString + " " + bundleName.substring(bundleName.lastIndexOf("/") + 1) + "::" + psiFile.getName(); + } + } + } + } + + return new NavigationItemPresentableOverwrite( + psiElement, + presentableText, + icon, + locationPathString, + name + ); + } +} + diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/navigation/RouteSymbolContributor.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/navigation/RouteSymbolContributor.java index 258aa9f8c..935723ac4 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/navigation/RouteSymbolContributor.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/navigation/RouteSymbolContributor.java @@ -33,11 +33,6 @@ public void processNames(@NotNull Processor processor, @NotNull for (Route route : routes.values()) { processor.process(route.getName()); - - String path = route.getPath(); - if (path != null) { - processor.process(path); - } } } @@ -51,9 +46,5 @@ public void processElementsWithName(@NotNull String name, @NotNull Processor + */ +public class RouteUrlMatcherSymbolContributor implements ChooseByNameContributorEx, ChooseByNameContributorEx2 { + + @Override + public void processNames(@NotNull Processor processor, @NotNull FindSymbolParameters parameters) { + Project project = parameters.getProject(); + if (!Symfony2ProjectComponent.isEnabled(project)) { + return; + } + + String name = parameters.getLocalPatternName(); + if (RouteHelper.hasRoutesForPathWithPlaceholderMatch(project, name)) { + processor.process(name); + } + } + + @Override + public void processNames(@NotNull Processor processor, @NotNull GlobalSearchScope scope, @Nullable IdFilter filter) { + } + + @Override + public void processElementsWithName(@NotNull String name, @NotNull Processor processor, @NotNull FindSymbolParameters parameters) { + Project project = parameters.getProject(); + if (!Symfony2ProjectComponent.isEnabled(project)) { + return; + } + + for (Pair entry : RouteHelper.getMethodsForPathWithPlaceholderMatchRoutes(project, name)) { + Route route = entry.getFirst(); + + String path = route.getPath(); + if (path == null) { + continue; + } + + processor.process((NavigationItemPresentableOverwrite.create( + entry.getSecond(), + path, + Symfony2Icons.ROUTE, + "Symfony Route", + true, + name + ))); + } + } +} diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/routing/RouteHelper.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/routing/RouteHelper.java index f8a41bb4b..f13ae20d5 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/routing/RouteHelper.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/routing/RouteHelper.java @@ -47,6 +47,7 @@ import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil; import fr.adrienbrault.idea.symfony2plugin.util.dict.SymfonyBundle; import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper; +import kotlin.Pair; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -148,6 +149,28 @@ public static Collection findRoutesByPath(@NotNull Project project, @NotN .collect(Collectors.toList()); } + /** + * Reverse routing matching, find any "incomplete" string inside the route pattern + * + * - "foo/bar" => "/foo/bar" + * - "foo/12" => "/foo/{edit}" + * - "ar/12/foo" => "/car/{edit}/foobar" + */ + @NotNull + public static Collection> getMethodsForPathWithPlaceholderMatchRoutes(@NotNull Project project, @NotNull String searchPath) { + Collection> targets = new ArrayList<>(); + + getRoutesForPathWithPlaceholderMatch(project, searchPath) + .forEach(route -> targets.addAll( + Arrays.stream(getMethodsOnControllerShortcut(project, route.getController())) + .map(psiElement -> new Pair<>(route, psiElement)) + .toList() + ) + ); + + return targets; + } + /** * Reverse routing matching, find any "incomplete" string inside the route pattern * @@ -157,56 +180,95 @@ public static Collection findRoutesByPath(@NotNull Project project, @NotN */ @NotNull public static PsiElement[] getMethodsForPathWithPlaceholderMatch(@NotNull Project project, @NotNull String searchPath) { - Set targets = new HashSet<>(); + Collection psiElements = new HashSet<>(); + for (Pair entry : RouteHelper.getMethodsForPathWithPlaceholderMatchRoutes(project, searchPath)) { + psiElements.add(entry.getSecond()); + } - for (Route route : RouteHelper.getAllRoutes(project).values()) { - String routePath = route.getPath(); - if (routePath == null) { - continue; - } + return psiElements.toArray(new PsiElement[0]); + } - if (routePath.contains(searchPath)) { - targets.addAll(Arrays.asList(getMethodsOnControllerShortcut(project, route.getController()))); - continue; - } + /** + * Reverse routing matching, find any "incomplete" string inside the route pattern + * + * - "foo/bar" => "/foo/bar" + * - "foo/12" => "/foo/{edit}" + * - "ar/12/foo" => "/car/{edit}/foobar" + */ + @NotNull + public static Collection getRoutesForPathWithPlaceholderMatch(@NotNull Project project, @NotNull String searchPath) { + Collection targets = new ArrayList<>(); + + RouteHelper.getAllRoutes(project).values() + .parallelStream() + .forEach(route -> { + if (isReverseRoutePatternMatch(route, searchPath)) { + targets.add(route); + } + }); - // String string = "|"; visibility debug - String string = Character.toString((char) 156); + return targets; + } - String routePathPlaceholderNeutral = routePath.replaceAll("\\{([^}]*)}", string); - String match = null; - int startIndex = -1; + /** + * Reverse routing matching, find any "incomplete" string inside the route pattern + * + * - "foo/bar" => "/foo/bar" + * - "foo/12" => "/foo/{edit}" + * - "ar/12/foo" => "/car/{edit}/foobar" + */ + public static boolean hasRoutesForPathWithPlaceholderMatch(@NotNull Project project, @NotNull String searchPath) { + return RouteHelper.getAllRoutes(project).values() + .parallelStream() + .anyMatch(route -> isReverseRoutePatternMatch(route, searchPath)); + } - // find first common non pattern string, string on at 2 for no fetching all; right to left - for (int i = 2; i < searchPath.length(); i++) { - String text = searchPath.substring(0, searchPath.length() - i); + private static boolean isReverseRoutePatternMatch(@NotNull Route route, @NotNull String searchPath) { + String routePath = route.getPath(); + if (routePath == null) { + return false; + } - int i1 = routePathPlaceholderNeutral.indexOf(text); - if (i1 >= 0) { - match = routePathPlaceholderNeutral.substring(i1); - startIndex = text.length(); - break; - } - } + if (routePath.contains(searchPath)) { + return true; + } - if (match == null) { - continue; + // String string = "|"; visibility debug + String string = Character.toString((char) 156); + + String routePathPlaceholderNeutral = routePath.replaceAll("\\{([^}]*)}", string); + String match = null; + int startIndex = -1; + + // find first common non pattern string, string on at 2 for no fetching all; right to left + for (int i = 2; i < searchPath.length(); i++) { + String text = searchPath.substring(0, searchPath.length() - i); + + int i1 = routePathPlaceholderNeutral.indexOf(text); + if (i1 >= 0) { + match = routePathPlaceholderNeutral.substring(i1); + startIndex = text.length(); + break; } + } - // find a pattern match: left to right - int endIndex = match.length(); - for (int i = startIndex + 1; i <= endIndex; i++) { - String substring = match.substring(0, i); + if (match == null) { + return false; + } - String regex = substring.replace(string, "[\\w-]+"); - Matcher matcher = Pattern.compile(regex).matcher(searchPath); - if (matcher.matches()) { - targets.addAll(Arrays.asList(getMethodsOnControllerShortcut(project, route.getController()))); - } + // find a pattern match: left to right + int endIndex = match.length(); + for (int i = startIndex + 1; i <= endIndex; i++) { + String substring = match.substring(0, i); + + String regex = substring.replace(string, "[\\w-]+"); + Matcher matcher = Pattern.compile(regex).matcher(searchPath); + if (matcher.matches()) { + return true; } } - return targets.toArray(new PsiElement[0]); + return false; } /** diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 03d25a742..3ee1dff4a 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -293,6 +293,7 @@ +