diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/xml/XmlHelper.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/xml/XmlHelper.java index ed4edb33b..80fc713a1 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/xml/XmlHelper.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/config/xml/XmlHelper.java @@ -15,6 +15,7 @@ import com.jetbrains.php.lang.psi.elements.Parameter; import com.jetbrains.php.lang.psi.elements.PhpClass; import fr.adrienbrault.idea.symfony2plugin.dic.ParameterResolverConsumer; +import fr.adrienbrault.idea.symfony2plugin.dic.container.util.ServiceContainerUtil; import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil; import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil; import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper; @@ -23,8 +24,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.Map; +import java.util.*; /** * @author Daniel Espendiller @@ -834,4 +834,42 @@ public static boolean isXmlFileExtension(@NotNull PsiFile psiFile) { VirtualFile virtualFile = psiFile.getVirtualFile(); return virtualFile == null || !"xml".equalsIgnoreCase(virtualFile.getExtension()); } + + /** + * " + * ../foobar" + * " + */ + public static @NotNull Collection getNamespaceResourcesClasses(@NotNull XmlTag xmlTag) { + String namespace = xmlTag.getAttributeValue("namespace"); + if (StringUtils.isBlank(namespace)) { + return Collections.emptyList(); + } + + String resource = xmlTag.getAttributeValue("resource"); + if (StringUtils.isBlank(resource)) { + return Collections.emptyList(); + } + + Set excludes = new HashSet<>(); + String exclude = xmlTag.getAttributeValue("exclude"); + if (StringUtils.isNotBlank(exclude)) { + excludes.add(exclude); + } + + for (XmlTag excludeTag : xmlTag.findSubTags("exclude")) { + String text = excludeTag.getValue().getText(); + if (StringUtils.isNotBlank(text)) { + excludes.add(text); + } + } + + return ServiceContainerUtil.getPhpClassFromResources( + xmlTag.getProject(), + namespace, + xmlTag.getContainingFile().getVirtualFile(), + List.of(resource), + excludes + ); + } } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/attribute/value/XmlTagAttributeValue.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/attribute/value/XmlTagAttributeValue.java index 785ba9451..bc095a52f 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/attribute/value/XmlTagAttributeValue.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/attribute/value/XmlTagAttributeValue.java @@ -5,9 +5,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; +import java.util.*; import java.util.stream.Collectors; /** @@ -25,11 +23,26 @@ public XmlTagAttributeValue(@NotNull XmlTag xmlTag) { @NotNull @Override public Collection getStringArray(@NotNull String key) { + Set values = new HashSet<>(); + String string = getString(key); + if (StringUtils.isNotBlank(string)) { + values.add(string); + } + + // " + // ../foobar" + // " + if (key.equals("exclude")) { + for (XmlTag excludeTag : xmlTag.findSubTags(key)) { + String text = excludeTag.getValue().getText(); + if (StringUtils.isNotBlank(text)) { + values.add(text); + } + } + } - return string != null - ? Collections.singleton(string) - : Collections.emptyList(); + return values; } @Nullable diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/linemarker/XmlLineMarkerProvider.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/linemarker/XmlLineMarkerProvider.java index fc283447d..9572d8076 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/linemarker/XmlLineMarkerProvider.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/linemarker/XmlLineMarkerProvider.java @@ -3,6 +3,8 @@ import com.intellij.codeInsight.daemon.LineMarkerInfo; import com.intellij.codeInsight.daemon.LineMarkerProvider; import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder; +import com.intellij.icons.AllIcons; +import com.intellij.openapi.util.NotNullLazyValue; import com.intellij.patterns.XmlPatterns; import com.intellij.patterns.XmlTagPattern; import com.intellij.psi.PsiElement; @@ -46,17 +48,42 @@ public void collectSlowLineMarkers(@NotNull List psiElemen continue; } + if(!XmlHelper.getXmlTagNameLeafStartPattern().accepts(psiElement)) { + continue; + } + PsiElement xmlTag = psiElement.getParent(); - if(!(xmlTag instanceof XmlTag) || !getServiceIdPattern().accepts(xmlTag)) { + if(!(xmlTag instanceof XmlTag)) { continue; } - if(lazyDecoratedParentServiceValues == null) { - lazyDecoratedParentServiceValues = new LazyDecoratedParentServiceValues(psiElement.getProject()); + if (getServiceIdPattern().accepts(xmlTag)) { + if(lazyDecoratedParentServiceValues == null) { + lazyDecoratedParentServiceValues = new LazyDecoratedParentServiceValues(psiElement.getProject()); + } + + // + visitServiceId(psiElement, (XmlTag) xmlTag, result, lazyDecoratedParentServiceValues); + + continue; } - // - visitServiceId(psiElement, (XmlTag) xmlTag, result, lazyDecoratedParentServiceValues); + if (getPrototypeNamespacePattern().accepts(xmlTag)) { + String namespace = ((XmlTag) xmlTag).getAttributeValue("namespace"); + if (StringUtils.isBlank(namespace)) { + continue; + } + + String resource = ((XmlTag) xmlTag).getAttributeValue("resource"); + if (StringUtils.isBlank(resource)) { + continue; + } + + result.add(NavigationGutterIconBuilder.create(AllIcons.Modules.SourceRoot) + .setTargets(NotNullLazyValue.lazy(() -> XmlHelper.getNamespaceResourcesClasses((XmlTag) xmlTag))) + .setTooltipText("Navigate to class") + .createLineMarkerInfo(psiElement)); + } } } @@ -115,4 +142,14 @@ private static XmlTagPattern.Capture getServiceIdPattern() { XmlHelper.getInsideTagPattern("services") ).inFile(XmlHelper.getXmlFilePattern()); } + + /** + * + */ + private static XmlTagPattern.Capture getPrototypeNamespacePattern() { + return XmlPatterns.xmlTag().withName("prototype") + .withChild(XmlPatterns.xmlAttribute().withName("namespace")).inside( + XmlHelper.getInsideTagPattern("services") + ).inFile(XmlHelper.getXmlFilePattern()); + } } diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/xml/XmlHelperTest.java b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/xml/XmlHelperTest.java index f289def33..edf13b092 100644 --- a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/xml/XmlHelperTest.java +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/xml/XmlHelperTest.java @@ -1,16 +1,24 @@ package fr.adrienbrault.idea.symfony2plugin.tests.config.xml; import com.intellij.ide.highlighter.XmlFileType; +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.util.PsiTreeUtil; import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlTag; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.php.lang.psi.elements.Parameter; +import com.jetbrains.php.lang.psi.elements.PhpClass; +import com.jetbrains.php.lang.psi.elements.PhpNamedElement; import fr.adrienbrault.idea.symfony2plugin.config.xml.XmlHelper; import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase; import java.util.ArrayList; import java.util.Collection; +import java.util.function.Predicate; +import java.util.stream.Collectors; /** * @author Daniel Espendiller @@ -169,4 +177,23 @@ public void testGetArgumentIndexOnArgumentCount() { PsiElement psiElement = myFixture.getFile().findElementAt(myFixture.getCaretOffset()); assertEquals(1, XmlHelper.getArgumentIndex((XmlTag) psiElement.getParent())); } + + /** + * @see XmlHelper#getNamespaceResourcesClasses + */ + public void testGetNamespaceResourcesClasses() { + myFixture.copyFileToProject("XmlHelper.php", "src/XmlHelper.php"); + VirtualFile service = myFixture.copyFileToProject("services.xml", "src/services.xml"); + + PsiFile file = PsiManager.getInstance(getProject()).findFile(service); + + Collection xmlTags = PsiTreeUtil.collectElementsOfType(file, XmlTag.class); + + XmlTag xmlTag = xmlTags.stream().filter(xmlTag1 -> "prototype".equals(xmlTag1.getName())).findFirst().orElseThrow(); + Collection namespaceResourcesClasses = XmlHelper.getNamespaceResourcesClasses(xmlTag).stream() + .map(PhpNamedElement::getFQN) + .collect(Collectors.toSet()); + + assertContainsElements(namespaceResourcesClasses, "\\Foo\\Bar"); + } } diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/xml/fixtures/services.xml b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/xml/fixtures/services.xml index 37d06a52f..d8b24284b 100644 --- a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/xml/fixtures/services.xml +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/xml/fixtures/services.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/ServicesDefinitionStubIndexTest.java b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/ServicesDefinitionStubIndexTest.java index 7e100a35d..9bddda1e6 100644 --- a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/ServicesDefinitionStubIndexTest.java +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/ServicesDefinitionStubIndexTest.java @@ -95,6 +95,7 @@ public void testThatResourceAndExcludeAttributesAreExtractedForXml() { assertEquals("App\\Xml\\", firstValue.getId()); assertContainsElements(firstValue.getResource(), "../src/*"); assertContainsElements(firstValue.getExclude(), "../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}"); + assertContainsElements(firstValue.getExclude(), "../src/foobar"); } public void testThatTagAreInIndexForYaml() { diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/fixtures/services.xml b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/fixtures/services.xml index f26a32bac..58177bc74 100644 --- a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/fixtures/services.xml +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/stubs/indexes/fixtures/services.xml @@ -35,7 +35,9 @@ + exclude="../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}"> + ../src/foobar +