8000 provide backslash normalizer and refactored routing name resolve rela… · Koc/idea-php-symfony2-plugin@32a2212 · GitHub
[go: up one dir, main page]

Skip to content

Commit 32a2212

Browse files
committed
provide backslash normalizer and refactored routing name resolve related code Twig path directory separator must be "\" to be clickable Haehnchen#995
1 parent f8fc247 commit 32a2212

File tree

10 files changed

+98
-57
lines changed

10 files changed

+98
-57
lines changed

src/fr/adrienbrault/idea/symfony2plugin/external/toolbox/provider/ControllerToolboxProvider.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
import de.espend.idea.php.toolbox.navigation.dict.PhpToolboxDeclarationHandlerParameter;
99
import de.espend.idea.php.toolbox.provider.presentation.ProviderPresentation;
1010
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
11+
import fr.adrienbrault.idea.symfony2plugin.routing.RouteHelper;
1112
import fr.adrienbrault.idea.symfony2plugin.util.controller.ControllerIndex;
1213
import org.jetbrains.annotations.NotNull;
1314
import org.jetbrains.annotations.Nullable;
1415

1516
import javax.swing.*;
1617
import java.util.ArrayList;
18+
import java.util.Arrays;
1719
import java.util.Collection;
1820
import java.util.Collections;
1921

@@ -38,9 +40,7 @@ public Collection<PsiElement> getPsiTargets(final @NotNull PhpToolboxDeclaration
3840
return Collections.emptyList();
3941
}
4042

41-
return new ArrayList<PsiElement>() {{
42-
add(ControllerIndex.getControllerMethod(parameter.getProject(), parameter.getContents()));
43-
}};
43+
return Arrays.asList(RouteHelper.getMethodsOnControllerShortcut(parameter.getProject(), parameter.getContents()));
4444
}
4545

4646
@NotNull

src/fr/adrienbrault/idea/symfony2plugin/routing/RouteHelper.java

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -175,25 +175,12 @@ public static PsiElement[] getMethodsOnControllerShortcut(@NotNull Project proje
175175
return method != null ? new PsiElement[] {method} : new PsiElement[0];
176176

177177
} else if(controllerName.contains(":")) {
178-
179178
// AcmeDemoBundle:Demo:hello
180179
String[] split = controllerName.split(":");
181180
if(split.length == 3) {
182-
183-
// try to resolve on bundle path
184-
SymfonyBundle symfonyBundle = new SymfonyBundleUtil(project).getBundle(split[0]);
185-
if(symfonyBundle != null) {
186-
// AcmeDemoBundle\Controller\DemoController:helloAction
187-
Method method = PhpElementsUtil.getClassMethod(project, symfonyBundle.getNamespaceName() + "Controller\\" + split[1] + "Controller", split[2] + "Action");
188-
if(method != null) {
189-
return new PsiElement[] {method};
190-
}
191-
}
192-
B421
193-
// fallback to controller class instances, if relative path doesnt follow default file structure
194-
Method method = ControllerIndex.getControllerMethod(project, controllerName);
195-
if(method != null) {
196-
return new PsiElement[] {method};
181+
Collection<Method> controllerMethod = ControllerIndex.getControllerMethod(project, controllerName);
182+
if(controllerMethod.size() > 0) {
183+
return controllerMethod.toArray(new PsiElement[controllerMethod.size()]);
197184
}
198185
}
199186

src/fr/adrienbrault/idea/symfony2plugin/templating/TwigTemplateGoToDeclarationHandler.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import fr.adrienbrault.idea.symfony2plugin.translation.dict.TranslationUtil;
2424
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
2525
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
26-
import fr.adrienbrault.idea.symfony2plugin.util.controller.ControllerIndex;
2726
import org.apache.commons.lang.StringUtils;
2827
import org.jetbrains.annotations.NotNull;
2928
import org.jetbrains.annotations.Nullable;
@@ -90,9 +89,9 @@ public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int i, Edit
9089
}
9190

9291
if(TwigHelper.getPrintBlockOrTagFunctionPattern("controller").accepts(psiElement) || TwigHelper.getStringAfterTagNamePattern("render").accepts(psiElement)) {
93-
PsiElement controllerMethod = this.getControllerGoTo(psiElement);
94-
if(controllerMethod != null) {
95-
return new PsiElement[] { c F438 ontrollerMethod };
92+
Collection<PsiElement> controllerMethod = this.getControllerGoTo(psiElement);
93+
if(controllerMethod.size() > 0) {
94+
return controllerMethod.toArray(new PsiElement[controllerMethod.size()]);
9695
}
9796
}
9897

@@ -169,9 +168,9 @@ private PsiElement[] getRouteParameterGoTo(PsiElement psiElement) {
169168
return RouteHelper.getRouteParameterPsiElements(psiElement.getProject(), routeName, psiElement.getText());
170169
}
171170

172-
private PsiElement getControllerGoTo(PsiElement psiElement) {
171+
private Collection<PsiElement> getControllerGoTo(@NotNull PsiElement psiElement) {
173172
String text = PsiElementUtils.trimQuote(psiElement.getText());
174-
return ControllerIndex.getControllerMethod(psiElement.getProject(), text);
173+
return Arrays.asList(RouteHelper.getMethodsOnControllerShortcut(psiElement.getProject(), text));
175174
}
176175

177176
@Nullable

src/fr/adrienbrault/idea/symfony2plugin/templating/TwigTemplateGoToLocalDeclarationHandler.java

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,11 @@
88
import com.intellij.patterns.PlatformPatterns;
99
import com.intellij.psi.PsiDirectory;
1010
import com.intellij.psi.PsiElement;
11-
import com.intellij.psi.PsiFile;
1211
import com.intellij.psi.PsiManager;
1312
import com.intellij.psi.util.PsiTreeUtil;
1413
import com.intellij.util.containers.ContainerUtil;
1514
import com.jetbrains.php.PhpIndex;
1615
import com.jetbrains.php.lang.psi.elements.Field;
17-
import com.jetbrains.php.lang.psi.elements.Method;
1816
import com.jetbrains.php.lang.psi.elements.PhpClass;
1917
import com.jetbrains.twig.TwigLanguage;
2018
import com.jetbrains.twig.TwigTokenTypes;
@@ -23,8 +21,8 @@
2321
import com.jetbrains.twig.elements.TwigTagWithFileReference;
2422
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
2523
import fr.adrienbrault.idea.symfony2plugin.TwigHelper;
24+
import fr.adrienbrault.idea.symfony2plugin.routing.RouteHelper;
2625
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigExtension;
27-
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigMacro;
2826
import fr.adrienbrault.idea.symfony2plugin.templating.dict.TwigSet;
2927
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigExtensionParser;
3028
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigTypeResolveUtil;
@@ -33,7 +31,6 @@
3331
import fr.adrienbrault.idea.symfony2plugin.templating.variable.collector.ControllerDocVariableCollector;
3432
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
3533
import fr.adrienbrault.idea.symfony2plugin.util.RegexPsiElementFilter;
36-
import fr.adrienbrault.idea.symfony2plugin.util.controller.ControllerIndex;
3734
import org.apache.commons.lang.StringUtils;
3835
import org.jetbrains.annotations.NotNull;
3936
import org.jetbrains.annotations.Nullable;
@@ -201,7 +198,7 @@ private Collection<PsiElement> getSeeDocTagTargets(@NotNull PsiElement psiElemen
201198
}
202199

203200
psiElements.addAll(PhpElementsUtil.getClassesInterface(psiElement.getProject(), content));
204-
ContainerUtil.addIfNotNull(psiElements, ControllerIndex.getControllerMethod(psiElement.getProject(), content));
201+
ContainerUtil.addAll(psiElements, RouteHelper.getMethodsOnControllerShortcut(psiElement.getProject(), content));
205202

206203
PsiDirectory parent = psiElement.getContainingFile().getParent();
207204
if(parent != null) {
@@ -311,12 +308,7 @@ private PsiElement[] getControllerNameGoto(PsiElement psiElement) {
311308

312309
String controllerName = matcher.group(1);
313310

314-
Method method = ControllerIndex.getControllerMethod(psiElement.getProject(), controllerName);
315-
if(method == null) {
316-
return new PsiElement[0];
317-
}
318-
319-
return new PsiElement[] { method };
311+
return RouteHelper.getMethodsOnControllerShortcut(psiElement.getProject(), controllerName);
320312
}
321313

322314
private PsiElement[] getParentGoto(PsiElement psiElement) {

src/fr/adrienbrault/idea/symfony2plugin/templating/variable/collector/ControllerDocVariableCollector.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,10 @@ public void collectVars(TwigFileVariableCollectorParameter parameter, Map<String
4141
ControllerIndex controllerIndex = new ControllerIndex(parameter.getProject());
4242

4343
for(String controllerName: controllerNames) {
44-
Method method = controllerIndex.resolveShortcutName(controllerName);
45-
if(method != null) {
44+
for(Method method : controllerIndex.resolveShortcutName(controllerName)) {
4645
variables.putAll(PhpMethodVariableResolveUtil.collectMethodVariables(method));
4746
}
4847
}
49-
5048
}
5149

5250
private static ArrayList<String> findFileControllerDocBlocks(TwigFile twigFile) {
Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
11
package fr.adrienbrault.idea.symfony2plugin.util.controller;
22

33
import com.jetbrains.php.lang.psi.elements.Method;
4+
import org.jetbrains.annotations.NotNull;
45

56
/**
67
* @author Daniel Espendiller <daniel@espendiller.net>
78
*/
89
public class ControllerAction {
10+
@NotNull
11+
final private String shortcutName;
912

10-
String shortcutName;
11-
Method method;
13+
@NotNull
14+
final private Method method;
1215

13-
public ControllerAction(String shortcutName, Method method) {
16+
public ControllerAction(@NotNull String shortcutName, @NotNull Method method) {
1417
this.shortcutName = shortcutName;
1518
this.method = method;
1619
}
1720

21+
@NotNull
1822
public Method getMethod() {
1923
return method;
2024
}
2125

26+
@NotNull
2227
public String getShortcutName() {
2328
return shortcutName;
2429
}
25-
2630
}
2731

src/fr/adrienbrault/idea/symfony2plugin/util/controller/ControllerIndex.java

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import fr.adrienbrault.idea.symfony2plugin.routing.RouteHelper;
1010
import fr.adrienbrault.idea.symfony2plugin.routing.dic.ServiceRouteContainer;
1111
import fr.adrienbrault.idea.symfony2plugin.stubs.ContainerCollectionResolver;
12+
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
1213
import fr.adrienbrault.idea.symfony2plugin.util.PhpIndexUtil;
1314
import fr.adrienbrault.idea.symfony2plugin.util.SymfonyBundleUtil;
1415
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil;
@@ -154,20 +155,59 @@ private Collection<ControllerAction> getServiceActionMethods(@NotNull Project pr
154155
return actions;
155156
}
156157

157-
@Nullable
158-
public Method resolveShortcutName(String controllerName) {
159-
ControllerIndex controllerIndex = new ControllerIndex(project);
160-
ControllerAction controllerAction = controllerIndex.getControllerAction(controllerName);
161-
if(controllerAction != null) {
162-
return controllerAction.getMethod();
158+
@NotNull
159+
public Collection<Method> resolveShortcutName(@NotNull String controllerName) {
160+
String[] split = controllerName.split(":");
161+
162+
// normalize: "FooBundle:Apple/Bar:foo" => FooBundle:Apple\Bar:foo
163+
// support: "FooBundle:Apple\Bar:foo" => FooBundle:Apple\Bar:foo\bar
164+
if(split.length == 3) {
165+
// normalize incoming path "/" => "\" this are PHP namespace but both supported
166+
split[1] = split[1].replaceAll("/+", "\\\\").replaceAll("\\\\+", "\\\\");
167+
split[2] = split[2].replaceAll("/+", "\\\\").replaceAll("\\\\+", "\\\\");
168+
169+
Collection<Method> methods = new HashSet<>();
170+
for (SymfonyBundle symfonyBundle : new SymfonyBundleUtil(project).getBundles()) {
171+
// Bundle matched "AppBundle"
172+
if(split[0].equalsIgnoreCase(symfonyBundle.getName())) {
173+
String namespace = split[1] + "\\" + split[2];
174+
175+
// last element is our method name
176+
int lastBackslash = namespace.lastIndexOf("\\");
177+
if(lastBackslash > 0) {
178+
String methodName = namespace.substring(lastBackslash + 1);
179+
180+
// AppBundle/Controller/FooController
181+
String className = symfonyBundle.getNamespaceName() + "Controller\\" + namespace.substring(0, lastBackslash) + "Controller";
182+
183+
for (PhpClass phpClass : PhpElementsUtil.getClassesInterface(project, className)) {
184+
185+
// cleanup action to support "fooAction" and "foo" methods
186+
if(methodName.endsWith("Action")) {
187+
methodName = methodName.substring(0, methodName.length() - "Action".length());
188+
}
189+
190+
// find method
191+
for (String string : new String[] {methodName, methodName + "Action"}) {
192+
Method methodByName = phpClass.findMethodByName(string);
193+
if(methodByName != null) {
194+
methods.add(methodByName);
195+
}
196+
}
197+
}
198+
}
199+
}
200+
}
201+
202+
return methods;
163203
}
164204

165-
controllerAction = controllerIndex.getControllerActionOnService(controllerName);
205+
ControllerAction controllerAction = new ControllerIndex(project).getControllerActionOnService(controllerName);
166206
if(controllerAction != null) {
167-
return controllerAction.getMethod();
207+
return Collections.singletonList(controllerAction.getMethod());
168208
}
169209

170-
return null;
210+
return Collections.emptyList();
171211
}
172212

173213
private ContainerCollectionResolver.LazyServiceCollector getLazyServiceCollector(Project project) {
@@ -189,9 +229,8 @@ static public List<LookupElement> getControllerLookupElements(Project project) {
189229
return lookupElements;
190230
}
191231

192-
@Nullable
193-
static public Method getControllerMethod(Project project, String controllerName) {
232+
@NotNull
233+
static public Collection<Method> getControllerMethod(@NotNull Project project, @NotNull String controllerName) {
194234
return new ControllerIndex(project).resolveShortcutName(controllerName);
195235
}
196-
197236
}

tests/fr/adrienbrault/idea/symfony2plugin/tests/routing/RouteHelperTest.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ public class RouteHelperTest extends SymfonyLightCodeInsightFixtureTestCase {
3333
public void setUp() throws Exception {
3434
super.setUp();
3535

36-
myFixture.configureFromExistingVirtualFile(myFixture.copyFileToProject("RouteHelper.php"));
37-
myFixture.configureFromExistingVirtualFile(myFixture.copyFileToProject("classes.php"));
36+
myFixture.copyFileToProject("RouteHelper.php");
37+
myFixture.copyFileToProject("classes.php");
38+
myFixture.copyFileToProject("RouteHelper.services.yml");
3839
}
3940

4041
protected String getTestDataPath() {
@@ -416,6 +417,14 @@ public void testGetMethodsOnControllerShortcutForControllerAsInvokeWithoutInvoke
416417
assertEquals("Bar", ((PhpClass) targets[0]).getName());
417418
}
418419

420+
/**
421+
* @see fr.adrienbrault.idea.symfony2plugin.routing.RouteHelper#getMethodsOnControllerShortcut
422+
*/
423+
public void testGetMethodsOnControllerShortcutForControllerAsService() {
424+
PsiElement[] targets = RouteHelper.getMethodsOnControllerShortcut(getProject(), "foobar_controller:indexAction");
425+
assertEquals("CarController", ((Method) targets[0]).getContainingClass().getName());
426+
}
427+
419428
@NotNull
420429
private XmlFile createXmlFile(@NotNull String content) {
421430
return (XmlFile) PsiFileFactory.getInstance(getProject()).createFileFromText("DUMMY__." + XmlFileType.INSTANCE.getDefaultExtension(), XmlFileType.INSTANCE, content);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
services:
2+
foobar_controller:
3+
class: MyFooBarBundle\Controller\CarController

tests/fr/adrienbrault/idea/symfony2plugin/tests/util/controller/ControllerIndexTest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,14 @@ public void testThatControllerActionsForBundlesAreGenerated() {
3636
actions.stream().findFirst().filter(action -> "FooBundle:Apple\\Bar\\Foo:foo".equals(action.getShortcutName()))
3737
);
3838
}
39+
40+
public void testThatSlashAndBackSlashAreNormalized() {
41+
assertEquals("fooAction", ControllerIndex.getControllerMethod(getProject(), "FooBundle:Apple\\Bar\\Foo:foo").iterator().next().getName());
42+
assertEquals("fooAction", ControllerIndex.getControllerMethod(getProject(), "FooBundle:Apple/Bar/Foo:foo").iterator().next().getName());
43+
assertEquals("fooAction", ControllerIndex.getControllerMethod(getProject(), "FooBundle:Apple/Bar:Foo/foo").iterator().next().getName());
44+
assertEquals("fooAction", ControllerIndex.getControllerMethod(getProject(), "FooBundle:Apple/Bar:Foo/fooAction").iterator().next().getName());
45+
46+
assertEquals("fooAction", ControllerIndex.getControllerMethod(getProject(), "FooBundle:Apple///Bar:Foo////foo").iterator().next().getName());
47+
assertEquals("fooAction", ControllerIndex.getControllerMethod(getProject(), "FooBundle:Apple\\\\\\\\Bar:Foo\\\\foo").iterator().next().getName());
48+
}
3949
}

0 commit comments

Comments
 (0)
0