diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/Settings.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/Settings.java index 9c03f6fe7..5a01195ff 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/Settings.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/Settings.java @@ -24,15 +24,6 @@ ) public class Settings implements PersistentStateComponent { - // Default Symfony 2, 3 and 4 paths - public static String[] DEFAULT_CONTAINER_PATHS = new String[] { - "app/cache/dev/appDevDebugProjectContainer.xml", - "var/cache/dev/appDevDebugProjectContainer.xml", - "var/cache/dev/srcDevDebugProjectContainer.xml", - "var/cache/dev/srcApp_KernelDevDebugContainer.xml", - "var/cache/dev/App_KernelDevDebugContainer.xml" // Symfony => 4 + flex - }; - // Default Symfony 2, 3 and 4 paths public static String[] DEFAULT_ROUTES = new String[] { "app/cache/dev/appDevUrlGenerator.php", diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/Symfony2ProjectComponent.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/Symfony2ProjectComponent.java index a843ec9c0..214acdf3a 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/Symfony2ProjectComponent.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/Symfony2ProjectComponent.java @@ -12,6 +12,7 @@ import com.intellij.openapi.wm.WindowManager; import com.intellij.psi.PsiElement; import fr.adrienbrault.idea.symfony2plugin.dic.ContainerFile; +import fr.adrienbrault.idea.symfony2plugin.dic.container.util.ServiceContainerUtil; import fr.adrienbrault.idea.symfony2plugin.extension.PluginConfigurationExtension; import fr.adrienbrault.idea.symfony2plugin.extension.ServiceContainerLoader; import fr.adrienbrault.idea.symfony2plugin.extension.ServiceContainerLoaderParameter; @@ -110,7 +111,7 @@ public Collection getContainerFiles() { } if(containerFiles.size() == 0) { - for (String s : Settings.DEFAULT_CONTAINER_PATHS) { + for (String s : ServiceContainerUtil.getContainerFiles(project)) { containerFiles.add(new ContainerFile(s)); } } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/container/util/ServiceContainerUtil.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/container/util/ServiceContainerUtil.java index 7b4b7a45f..573a03f21 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/container/util/ServiceContainerUtil.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/container/util/ServiceContainerUtil.java @@ -1,11 +1,15 @@ package fr.adrienbrault.idea.symfony2plugin.dic.container.util; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.ModificationTracker; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.patterns.PlatformPatterns; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.*; import com.intellij.psi.xml.*; import com.intellij.util.Consumer; import com.intellij.util.indexing.FileBasedIndex; @@ -37,7 +41,10 @@ import org.jetbrains.yaml.YAMLUtil; import org.jetbrains.yaml.psi.*; +import java.time.Instant; import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * @author Daniel Espendiller @@ -55,6 +62,11 @@ public class ServiceContainerUtil { new MethodMatcher.CallToSignature("\\Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController", "get"), }; + private static ModificationTracker TIMED_MODIFICATION_TRACKER = new TimeSecondModificationTracker(60); + + private static final Key>> SYMFONY_COMPILED_TIMED_SERVICE_WATCHER = new Key<>("SYMFONY_COMPILED_TIMED_SERVICE_WATCHER"); + private static final Key>> SYMFONY_COMPILED_SERVICE_WATCHER = new Key<>("SYMFONY_COMPILED_SERVICE_WATCHER"); + private static String[] LOWER_PRIORITY = new String[] { "debug", "default", "abstract", "inner", "chain", "decorate", "delegat" }; @@ -627,4 +639,100 @@ public static List getSortedServiceId(@NotNull Project project, @NotNull return myIds; } + + /** + * Provide a modification on nearest second value + */ + private static class TimeSecondModificationTracker implements ModificationTracker { + private final int expiresAfter; + + public TimeSecondModificationTracker(int expiresAfter) { + this.expiresAfter = expiresAfter; + } + + @Override + public long getModificationCount() { + long unixTime = Instant.now().getEpochSecond(); + return roundNearest(unixTime); + } + + private long roundNearest(long n) { + // Smaller multiple + long a = (n / this.expiresAfter) * this.expiresAfter; + + // Larger multiple + long b = a + this.expiresAfter; + + // Return of closest of two + return (n - a > b - n) ? b : a; + } + } + + /** + * Find compiled and cache it until any psi change occur + * + * - "app/cache/dev/appDevDebugProjectContainer.xml" + * - ... + */ + public static Collection getContainerFiles(@NotNull Project project) { + return CachedValuesManager.getManager(project) + .getCachedValue( + project, + SYMFONY_COMPILED_SERVICE_WATCHER, + () -> CachedValueProvider.Result.create(getContainerFilesInner(project), PsiModificationTracker.MODIFICATION_COUNT), + false + ); + } + + /** + * Find possible compiled service file with seconds cache + * + * - "app/cache/dev/appDevDebugProjectContainer.xml" + * - "var/cache/dev/appDevDebugProjectContainer.xml" + * - "var/cache/dev/srcDevDebugProjectContainer.xml" + * - "var/cache/dev/srcApp_KernelDevDebugContainer.xml" + * - "var/cache/dev/App_KernelDevDebugContainer.xml" // Symfony => 4 + flex + * - "app/cache/dev_392373729/appDevDebugProjectContainer.xml" + */ + private static Collection getContainerFilesInner(@NotNull Project project) { + return CachedValuesManager.getManager(project).getCachedValue(project, SYMFONY_COMPILED_TIMED_SERVICE_WATCHER, () -> { + Set files = new HashSet<>(); + + // several Symfony cache folder structures + for (String root : new String[] {"var/cache", "app/cache"}) { + VirtualFile baseDir = project.getBaseDir(); + + VirtualFile relativeFile = VfsUtil.findRelativeFile(root, baseDir); + if (relativeFile == null) { + continue; + } + + // find a dev folder eg: "dev_392373729" or just "dev" + Set devFolders = Stream.of(relativeFile.getChildren()) + .filter(virtualFile -> virtualFile.isDirectory() && virtualFile.getName().toLowerCase().startsWith("dev")) + .collect(Collectors.toSet()); + + for (VirtualFile devFolder : devFolders) { + Set debugContainers = Stream.of(devFolder.getChildren()) + .filter(virtualFile -> { + if (!"xml".equalsIgnoreCase(virtualFile.getExtension())) { + return false; + } + + // Some examples: App_KernelDevDebugContainer, appDevDebugProjectContainer + String filename = virtualFile.getName().toLowerCase(); + return filename.contains("debugcontainer") + || (filename.contains("debug") && filename.contains("container")) + || (filename.contains("kernel") && filename.contains("container")); + }) + .map(virtualFile -> VfsUtil.getRelativePath(virtualFile, baseDir, '/')) + .collect(Collectors.toSet()); + + files.addAll(debugContainers); + } + } + + return CachedValueProvider.Result.create(files, TIMED_MODIFICATION_TRACKER); + }, false); + } } diff --git a/src/main/java/fr/adrienbrault/idea/symfony2plugin/ui/ContainerSettingsForm.java b/src/main/java/fr/adrienbrault/idea/symfony2plugin/ui/ContainerSettingsForm.java index 5b510a094..e42ed1b3f 100644 --- a/src/main/java/fr/adrienbrault/idea/symfony2plugin/ui/ContainerSettingsForm.java +++ b/src/main/java/fr/adrienbrault/idea/symfony2plugin/ui/ContainerSettingsForm.java @@ -14,6 +14,7 @@ import com.jetbrains.plugins.webDeployment.config.WebServerConfig; import fr.adrienbrault.idea.symfony2plugin.Settings; import fr.adrienbrault.idea.symfony2plugin.dic.ContainerFile; +import fr.adrienbrault.idea.symfony2plugin.dic.container.util.ServiceContainerUtil; import fr.adrienbrault.idea.symfony2plugin.ui.utils.UiSettingsUtil; import fr.adrienbrault.idea.symfony2plugin.ui.utils.dict.UiPathColumnInfo; import fr.adrienbrault.idea.symfony2plugin.ui.utils.dict.WebServerFileDialogExtensionCallback; @@ -68,7 +69,7 @@ public void mouseClicked(MouseEvent e) { resetContainerList(); // add default path - for (String defaultContainerPath : Settings.DEFAULT_CONTAINER_PATHS) { + for (String defaultContainerPath : ServiceContainerUtil.getContainerFiles(project)) { ContainerSettingsForm.this.modelList.addRow(new ContainerFile(defaultContainerPath)); }