Skip to content

support more use cases for compiled service path detection #1440

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,6 @@
)
public class Settings implements PersistentStateComponent<Settings> {

// 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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -110,7 +111,7 @@ public Collection<File> getContainerFiles() {
}

if(containerFiles.size() == 0) {
for (String s : Settings.DEFAULT_CONTAINER_PATHS) {
for (String s : ServiceContainerUtil.getContainerFiles(project)) {
containerFiles.add(new ContainerFile(s));
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 <daniel@espendiller.net>
Expand All @@ -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<CachedValue<Collection<String>>> SYMFONY_COMPILED_TIMED_SERVICE_WATCHER = new Key<>("SYMFONY_COMPILED_TIMED_SERVICE_WATCHER");
private static final Key<CachedValue<Collection<String>>> SYMFONY_COMPILED_SERVICE_WATCHER = new Key<>("SYMFONY_COMPILED_SERVICE_WATCHER");

private static String[] LOWER_PRIORITY = new String[] {
"debug", "default", "abstract", "inner", "chain", "decorate", "delegat"
};
Expand Down Expand Up @@ -627,4 +639,100 @@ public static List<String> 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<String> 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<String> getContainerFilesInner(@NotNull Project project) {
return CachedValuesManager.getManager(project).getCachedValue(project, SYMFONY_COMPILED_TIMED_SERVICE_WATCHER, () -> {
Set<String> 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<VirtualFile> devFolders = Stream.of(relativeFile.getChildren())
.filter(virtualFile -> virtualFile.isDirectory() && virtualFile.getName().toLowerCase().startsWith("dev"))
.collect(Collectors.toSet());

for (VirtualFile devFolder : devFolders) {
Set<String> 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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
}

Expand Down