diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/resource/FileResourceUtil.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/resource/FileResourceUtil.java index a7ad6e4b2..f4d224bd8 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/resource/FileResourceUtil.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/util/resource/FileResourceUtil.java @@ -10,7 +10,6 @@ import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiManager; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; @@ -18,6 +17,8 @@ import com.intellij.util.indexing.FileBasedIndex; import com.jetbrains.php.PhpIcons; import fr.adrienbrault.idea.symfony2plugin.stubs.cache.FileIndexCaches; +import fr.adrienbrault.idea.symfony2plugin.stubs.dict.FileResource; +import fr.adrienbrault.idea.symfony2plugin.stubs.dict.FileResourceContextTypeEnum; import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.FileResourcesIndex; import fr.adrienbrault.idea.symfony2plugin.util.FileResourceVisitorUtil; import fr.adrienbrault.idea.symfony2plugin.util.PhpIndexUtil; @@ -33,6 +34,7 @@ import java.nio.file.*; import java.nio.file.attribute.BasicFileAttributes; import java.util.*; +import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.PatternSyntaxException; import java.util.stream.Collectors; @@ -48,31 +50,6 @@ public class FileResourceUtil { */ private static final String[] GLOB_DETECTION_CHARS = {"*", "?", "{", "["}; - /** - * Search for files refers to given file - */ - @NotNull - public static Collection getFileResourceRefers(@NotNull Project project, @NotNull VirtualFile virtualFile) { - String bundleLocateName = getBundleLocateName(project, virtualFile); - if(bundleLocateName == null) { - return Collections.emptyList(); - } - - return getFileResourceRefers(project, bundleLocateName); - } - - /** - * Search for files refers to given file - */ - @NotNull - private static Collection getFileResourceRefers(@NotNull Project project, @NotNull String bundleLocateName) { - return FileBasedIndex.getInstance().getContainingFiles( - FileResourcesIndex.KEY, - bundleLocateName, - GlobalSearchScope.allScope(project) - ); - } - /** * Search for files refers to given file */ @@ -85,66 +62,14 @@ public static boolean hasFileResources(@NotNull Project project, @NotNull PsiFil return CachedValueProvider.Result.create(Boolean.FALSE, FileIndexCaches.getModificationTrackerForIndexId(project, FileResourcesIndex.KEY)); } - Set resources = new HashSet<>(FileBasedIndex.getInstance().getAllKeys(FileResourcesIndex.KEY, project)); + final Boolean[] aFalse = {Boolean.FALSE}; - for (String resource : resources) { - for (VirtualFile containingFile : FileBasedIndex.getInstance().getContainingFiles(FileResourcesIndex.KEY, resource, GlobalSearchScope.allScope(project))) { - VirtualFile directory = containingFile.getParent(); - if (directory == null) { - continue; - } - - String resourceResolved = resource; - - if (resource.startsWith("@")) { - String replace = resource.replace("\\", "/"); - int i = replace.indexOf("/"); - if (i > 2) { - String substring = resource.substring(1, i); - Collection bundle = new SymfonyBundleUtil(project).getBundle(substring); - - for (SymfonyBundle symfonyBundle : bundle) { - PsiDirectory directory1 = symfonyBundle.getDirectory(); - if (directory1 == null) { - continue; - } - - String substring1 = resource.substring(i); - String path = directory1.getVirtualFile().getPath(); - resourceResolved = path + substring1; - - break; - } - } - } - - if (Arrays.stream(GLOB_DETECTION_CHARS).anyMatch(resource::contains)) { - String path = directory.getPath(); - - // nested types not support by java glob implementation so just catch the exception: "../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php,Service/{IspConfiguration,DataCollection}}" - try { - String s1 = Paths.get(path + File.separatorChar + StringUtils.stripStart(resourceResolved, "\\/")).normalize().toString(); - String syntaxAndPattern = "glob:" + s1; - if (FileSystems.getDefault().getPathMatcher(syntaxAndPattern).matches(Paths.get(virtualFile.getPath()))) { - return CachedValueProvider.Result.create(Boolean.TRUE, FileIndexCaches.getModificationTrackerForIndexId(project, FileResourcesIndex.KEY)); - } - } catch (PatternSyntaxException | InvalidPathException ignored) { - } - - continue; - } - - VirtualFile relativeFile = VfsUtil.findRelativeFile(directory, resourceResolved.replace("\\", "/").split("/")); - if (relativeFile != null) { - String relativePath = VfsUtil.getRelativePath(virtualFile, relativeFile); - if (relativePath != null) { - return CachedValueProvider.Result.create(Boolean.TRUE, FileIndexCaches.getModificationTrackerForIndexId(project, FileResourcesIndex.KEY)); - } - } - } - } + visitFileResources(project, virtualFile, pair -> { + aFalse[0] = Boolean.TRUE; + return true; + }); - return CachedValueProvider.Result.create(Boolean.FALSE, FileIndexCaches.getModificationTrackerForIndexId(project, FileResourcesIndex.KEY)); + return CachedValueProvider.Result.create(aFalse[0], FileIndexCaches.getModificationTrackerForIndexId(project, FileResourcesIndex.KEY)); } ); } @@ -154,11 +79,32 @@ public static boolean hasFileResources(@NotNull Project project, @NotNull PsiFil */ @NotNull public static Collection> getFileResources(@NotNull Project project, @NotNull VirtualFile virtualFile) { - Set resources = new HashSet<>(FileBasedIndex.getInstance().getAllKeys(FileResourcesIndex.KEY, project)); - Collection> files = new ArrayList<>(); - for (String resource : resources) { - for (VirtualFile containingFile : FileBasedIndex.getInstance().getContainingFiles(FileResourcesIndex.KEY, resource, GlobalSearchScope.allScope(project))) { + + visitFileResources(project, virtualFile, pair -> { + files.add(Pair.create(pair.getFirst(), pair.getSecond())); + return false; + }); + + return files; + } + + /** + * Search for files refers to given file + */ + public static void visitFileResources(@NotNull Project project, @NotNull VirtualFile virtualFile, @NotNull Function, Boolean> consumer) { + Set files = new HashSet<>(); + for (String resource : FileBasedIndex.getInstance().getAllKeys(FileResourcesIndex.KEY, project)) { + files.addAll(FileBasedIndex.getInstance().getContainingFiles(FileResourcesIndex.KEY, resource, GlobalSearchScope.allScope(project))); + } + + for (VirtualFile containingFile : files) { + for (FileResource fileResourceContext : FileBasedIndex.getInstance().getFileData(FileResourcesIndex.KEY, containingFile, project).values()) { + String resource = fileResourceContext.getResource(); + if (resource == null) { + continue; + } + VirtualFile directory = containingFile.getParent(); if (directory == null) { continue; @@ -169,7 +115,14 @@ public static Collection> getFileResources(@NotNull Pr if (resource.startsWith("@")) { String replace = resource.replace("\\", "/"); int i = replace.indexOf("/"); - if (i > 2) { + + boolean resolved = false; + + if (i > 2 || i == -1) { + if (i == -1) { + i = resource.length(); + } + String substring = resource.substring(1, i); Collection bundle = new SymfonyBundleUtil(project).getBundle(substring); @@ -179,15 +132,22 @@ public static Collection> getFileResources(@NotNull Pr continue; } - String substring1 = resource.substring(i); - String path = directory1.getVirtualFile().getPath(); - resourceResolved = path + substring1; + resourceResolved = resource.substring(replace.contains("/") ? i + 1 : replace.length()); + directory = directory1.getVirtualFile(); + + resolved = true; break; } } + + if (!resolved) { + continue; + } } + // '../src/{Entity}' + // '../src/*Controller.php' if (Arrays.stream(GLOB_DETECTION_CHARS).anyMatch(resource::contains)) { String path = directory.getPath(); @@ -196,7 +156,9 @@ public static Collection> getFileResources(@NotNull Pr String s1 = Paths.get(path + File.separatorChar + StringUtils.stripStart(resourceResolved, "\\/")).normalize().toString(); String syntaxAndPattern = "glob:" + s1; if (FileSystems.getDefault().getPathMatcher(syntaxAndPattern).matches(Paths.get(virtualFile.getPath()))) { - files.add(Pair.create(containingFile, resource)); + if (consumer.apply(new Pair<>(containingFile, resource))) { + return; + } } } catch (PatternSyntaxException | InvalidPathException ignored) { } @@ -204,54 +166,31 @@ public static Collection> getFileResources(@NotNull Pr continue; } + // '../src/FooController.php' VirtualFile relativeFile = VfsUtil.findRelativeFile(directory, resourceResolved.replace("\\", "/").split("/")); - if (relativeFile != null) { - String relativePath = VfsUtil.getRelativePath(virtualFile, relativeFile); - if (relativePath != null) { - files.add(Pair.create(containingFile, resource)); + if (relativeFile != null && relativeFile.equals(virtualFile)) { + if (consumer.apply(new Pair<>(containingFile, resource))) { + return; } } - } - } - return files; - } - - @Nullable - public static String getBundleLocateName(@NotNull Project project, @NotNull VirtualFile virtualFile) { - SymfonyBundle containingBundle = new SymfonyBundleUtil(project).getContainingBundle(virtualFile); - if(containingBundle == null) { - return null; - } - - String relativePath = containingBundle.getRelativePath(virtualFile); - if(relativePath == null) { - return null; - } - - return "@" + containingBundle.getName() + "/" + relativePath; - } - - /** - * Search for line definition of "@FooBundle/foo.xml" - */ - @NotNull - private static Collection getBundleLocateStringDefinitions(@NotNull Project project, final @NotNull String bundleFileName) { - final Collection psiElements = new HashSet<>(); - for (VirtualFile refVirtualFile : getFileResourceRefers(project, bundleFileName)) { - PsiFile psiFile = PsiManager.getInstance(project).findFile(refVirtualFile); - if(psiFile == null) { - continue; - } + // '..src/Controller' + if (fileResourceContext.getContextType() == FileResourceContextTypeEnum.ROUTE) { + if (StringUtils.isNotBlank(resourceResolved)) { + directory = VfsUtil.findRelativeFile(directory, resourceResolved.replace("\\", "/").split("/")); + if (directory == null) { + continue; + } + } - FileResourceVisitorUtil.visitFile(psiFile, consumer -> { - if (bundleFileName.equals(consumer.getResource())) { - psiElements.add(consumer.getPsiElement()); + if (VfsUtil.isAncestor(directory, virtualFile, false)) { + if (consumer.apply(new Pair<>(containingFile, resource))) { + return; + } + } } - }); + } } - - return psiElements; } @Nullable @@ -263,15 +202,6 @@ public static RelatedItemLineMarkerInfo getFileImplementsLineMarker( return null; } - String bundleLocateName = FileResourceUtil.getBundleLocateName(project, virtualFile); - if(bundleLocateName != null && FileResourceUtil.getFileResourceRefers(project, bundleLocateName).size() > 0) { - NavigationGutterIconBuilder builder = NavigationGutterIconBuilder.create(PhpIcons.IMPLEMENTS) - .setTargets(NotNullLazyValue.lazy(new FileResourceBundleNotNullLazyValue(project, bundleLocateName))) - .setTooltipText("Navigate to resource"); - - return builder.createLineMarkerInfo(psiFile); - } - if (hasFileResources(project, psiFile)) { NavigationGutterIconBuilder builder = NavigationGutterIconBuilder.create(PhpIcons.IMPLEMENTS) .setTargets(NotNullLazyValue.lazy(new FileResourceNotNullLazyValue(project, virtualFile))) @@ -288,69 +218,22 @@ public static RelatedItemLineMarkerInfo getFileImplementsLineMarker( */ @Nullable public static RelatedItemLineMarkerInfo getFileImplementsLineMarkerInFolderScope(@NotNull PsiFile psiFile) { - VirtualFile virtualFile = psiFile.getVirtualFile(); - if(virtualFile == null) { - return null; - } - - final Project project = psiFile.getProject(); - String bundleLocateName = FileResourceUtil.getBundleLocateName(project, virtualFile); - if(bundleLocateName == null) { + if (!hasFileResources(psiFile.getProject(), psiFile)) { return null; } - Set names = new HashSet<>(); - names.add(bundleLocateName); - - // strip filename - int i = bundleLocateName.lastIndexOf("/"); - if(i > 0) { - names.add(bundleLocateName.substring(0, i)); - } - - int targets = 0; - for (String name : names) { - targets += FileResourceUtil.getFileResourceRefers(project, name).size(); - } - - if(targets == 0) { + VirtualFile virtualFile = psiFile.getVirtualFile(); + if (virtualFile == null) { return null; } NavigationGutterIconBuilder builder = NavigationGutterIconBuilder.create(PlatformIcons.ANNOTATION_TYPE_ICON) - .setTargets(NotNullLazyValue.lazy(new FileResourceBundleNotNullLazyValue(project, names))) + .setTargets(NotNullLazyValue.lazy(new FileResourceNotNullLazyValue(psiFile.getProject(), virtualFile))) .setTooltipText("Symfony: Annotation Routing"); return builder.createLineMarkerInfo(psiFile); } - private static class FileResourceBundleNotNullLazyValue implements Supplier> { - - private final Collection resources; - private final Project project; - - public FileResourceBundleNotNullLazyValue(@NotNull Project project, @NotNull Collection resource) { - this.resources = resource; - this.project = project; - } - - public FileResourceBundleNotNullLazyValue(@NotNull Project project, @NotNull String resource) { - this.resources = Collections.singleton(resource); - this.project = project; - } - - @Override - public Collection get() { - Collection psiElements = new HashSet<>(); - - for (String resource : this.resources) { - psiElements.addAll(getBundleLocateStringDefinitions(project, resource)); - } - - return psiElements; - } - } - /** * Gives targets to files on Bundle locate syntax. "@FooBundle/.../foo.yml" */ @@ -474,7 +357,7 @@ private static class FileResourceNotNullLazyValue implements Supplier providers = new ArrayList() {{ - add(new String[] {"@FooBundle/foo.php"}); - add(new String[] {"@FooBundle"}); - add(new String[] {"@FooBundle/"}); - }}; + String[] providers = new String[] {"@FooBundle/foo.php", "@FooBundle", "@FooBundle/"}; - for (String[] provider : providers) { + for (String provider : providers) { myFixture.configureByText( XmlFileType.INSTANCE, - String.format("", provider[0] ) + String.format("", provider) ); PsiFile psiFile = myFixture.configureByText("foo.php", ""); @@ -101,7 +97,7 @@ public void testThatResourceProvidesLineMarker() { psiFile, new LineMarker.TargetAcceptsPattern( "Symfony: Annotation Routing", - XmlPatterns.xmlTag().withName("import").withAttributeValue("resource", provider[0]) + XmlPatterns.xmlTag().withName("import").withAttributeValue("resource", provider) ) ); } diff --git a/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/fixtures/dummy.php b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/fixtures/dummy.php new file mode 100644 index 000000000..a81436628 --- /dev/null +++ b/src/test/java/fr/adrienbrault/idea/symfony2plugin/tests/config/fixtures/dummy.php @@ -0,0 +1 @@ + virtualFile.getName().equals("target.xml"))); - } - public void testGetFileResourceTargetsInBundleDirectory() { createBundleScopeProject(); @@ -78,7 +71,7 @@ public void testFileResources() { } public void testFileResourcesForBundle() { - myFixture.copyFileToProject("classes.php"); + myFixture.copyFileToProject("classes.php", "src/classes.php"); myFixture.configureByText("target.xml", "" + "\n" + " \n" +