Skip to content

fix assets reading and provide explicit resolving for asset files instead of rescanning them to improve performance an drop massive opendirectoryd cpu time #809 #1118 #1119

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
Dec 21, 2017
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
@@ -0,0 +1,200 @@
package fr.adrienbrault.idea.symfony2plugin.asset;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileVisitor;
import com.intellij.psi.PsiDirectory;
import com.jetbrains.php.PhpIndex;
import fr.adrienbrault.idea.symfony2plugin.Settings;
import fr.adrienbrault.idea.symfony2plugin.util.SymfonyBundleUtil;
import fr.adrienbrault.idea.symfony2plugin.util.dict.SymfonyBundle;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class AssetDirectoryReader {

public static AssetDirectoryReader INSTANCE = new AssetDirectoryReader();

final private boolean includeBundleDir;

@NotNull
final private Collection<String> filterExtension = new HashSet<>();

public AssetDirectoryReader() {
includeBundleDir = false;
}

public AssetDirectoryReader(@NotNull String[] filterExtension, boolean includeBundleDir) {
this.includeBundleDir = includeBundleDir;
this.filterExtension.addAll(Arrays.asList(filterExtension));
}

@Nullable
private static VirtualFile getProjectAssetRoot(@NotNull Project project) {
VirtualFile projectDirectory = project.getBaseDir();
String webDirectoryName = Settings.getInstance(project).directoryToWeb;
return VfsUtil.findRelativeFile(projectDirectory, webDirectoryName.split("/"));
}

@NotNull
public Collection<AssetFile> getAssetFiles(@NotNull Project project) {
Collection<AssetFile> files = new ArrayList<>();

VirtualFile webDirectory = getProjectAssetRoot(project);
if (null == webDirectory) {
return files;
}

VfsUtil.visitChildrenRecursively(webDirectory, new VirtualFileVisitor() {
@Override
public boolean visitFile(@NotNull VirtualFile virtualFile) {
if(isValidFile(virtualFile)) {
files.add(new AssetFile(virtualFile, AssetEnum.Position.Web, webDirectory));
}
return super.visitFile(virtualFile);
}
});

if(!this.includeBundleDir) {
return files;
}

SymfonyBundleUtil symfonyBundleUtil = new SymfonyBundleUtil(PhpIndex.getInstance(project));
for(SymfonyBundle bundle : symfonyBundleUtil.getBundles()) {
PsiDirectory bundleDirectory = bundle.getDirectory();
if(null == bundleDirectory) {
continue;
}

VirtualFile bundleDirectoryVirtual = bundleDirectory.getVirtualFile();
VirtualFile resourceDirectory = VfsUtil.findRelativeFile(bundleDirectoryVirtual, "Resources");

if (null != resourceDirectory) {
VfsUtil.visitChildrenRecursively(resourceDirectory, new VirtualFileVisitor() {
@Override
public boolean visitFile(@NotNull VirtualFile virtualFile) {
if(isValidFile(virtualFile)) {
files.add(new AssetFile(virtualFile, AssetEnum.Position.Bundle, bundleDirectoryVirtual, '@' + bundle.getName() + "/"));
}
return super.visitFile(virtualFile);
}
});
}
}

return files;
}

/**
* '@SampleBundle/Resources/public/js/*'
* 'assets/js/*'
* 'assets/js/*.js'
*/
@NotNull
public Collection<VirtualFile> resolveAssetFile(@NotNull Project project, @NotNull String filename) {
String assetName = StringUtils.stripStart(filename.replace("\\", "/").replaceAll("/+", "/"), "/");

// '@SampleBundle/Resources/public/js/foo,js'
// TODO: '@SampleBundle/Resources/public/js/*'
// TODO: '@SampleBundle/Resources/public/js/*.js'
if(filename.startsWith("@")) {
Collection<VirtualFile> files = new ArrayList<>();

int i = filename.indexOf("/");
if(i > 0) {
String relativeFilename = filename.substring(1, i);
SymfonyBundle bundle = new SymfonyBundleUtil(PhpIndex.getInstance(project)).getBundle(relativeFilename);
if(bundle != null) {
String assetPath = filename.substring(i + 1);

Matcher matcher = Pattern.compile("^(.*[/\\\\])\\*([.\\w+]*)$").matcher(assetPath);
if (!matcher.find()) {
VirtualFile relative = bundle.getRelative(assetPath);
if(relative != null) {
files.add(relative);
}
} else {
// "/*"
// "/*.js"
PsiDirectory directory = bundle.getDirectory();
if(directory != null) {
files.addAll(collectWildcardDirectories(matcher, directory.getVirtualFile()));
}
}
}
}

return files;
}

Collection<VirtualFile> files = new ArrayList<>();

VirtualFile webDirectory = getProjectAssetRoot(project);
if (null == webDirectory) {
return files;
}

Matcher matcher = Pattern.compile("^(.*[/\\\\])\\*([.\\w+]*)$").matcher(assetName);
if (!matcher.find()) {
VirtualFile assetFile = VfsUtil.findRelativeFile(webDirectory, assetName.split("/"));
if(assetFile != null) {
files.add(assetFile);
}
} else {
// "/*"
// "/*.js"
files.addAll(collectWildcardDirectories(matcher, webDirectory));
}

return files;
}

private Collection<VirtualFile> collectWildcardDirectories(@NotNull Matcher matcher, @NotNull VirtualFile directory) {
Collection<VirtualFile> files = new HashSet<>();

String pathName = matcher.group(1);
String fileExtension = matcher.group(2).length() > 0 ? matcher.group(2) : null;

pathName = StringUtils.stripEnd(pathName, "/");

if(fileExtension == null) {
// @TODO: filter files
// 'assets/js/*'
VirtualFile assetFile = VfsUtil.findRelativeFile(directory, pathName.split("/"));
if(assetFile != null) {
files.add(assetFile);
}
} else {
// @TODO: filter files
// 'assets/js/*.js'
VirtualFile assetFile = VfsUtil.findRelativeFile(directory, pathName.split("/"));
if(assetFile != null) {
files.add(assetFile);
}
}

return files;
}

private boolean isValidFile(@NotNull VirtualFile virtualFile) {
if (virtualFile.isDirectory()) {
return false;
}

if(filterExtension.size() == 0) {
return true;
}

String extension = virtualFile.getExtension();
return extension != null && this.filterExtension.contains(extension);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package fr.adrienbrault.idea.symfony2plugin.asset.dic;
package fr.adrienbrault.idea.symfony2plugin.asset;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
Expand Down
46 changes: 46 additions & 0 deletions src/fr/adrienbrault/idea/symfony2plugin/asset/AssetFile.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package fr.adrienbrault.idea.symfony2plugin.asset;

import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
*/
public class AssetFile {
@NotNull
private VirtualFile assetFile;

@NotNull
private AssetEnum.Position assetPosition;

@NotNull
private VirtualFile relativeFolder;

private String prefix = "";

public AssetFile(@NotNull VirtualFile assetFile, @NotNull AssetEnum.Position assetPosition, @NotNull VirtualFile relativeFolder, @NotNull String prefix) {
this(assetFile, assetPosition, relativeFolder);
this.prefix = prefix;
}

public AssetFile(@NotNull VirtualFile assetFile, @NotNull AssetEnum.Position assetPosition, @NotNull VirtualFile relativeFolder) {
this.assetFile = assetFile;
this.assetPosition = assetPosition;
this.relativeFolder = relativeFolder;
}

@NotNull
public VirtualFile getFile() {
return assetFile;
}

@NotNull
public AssetEnum.Position getAssetPosition() {
return assetPosition;
}

public String toString() {
return this.prefix + VfsUtil.getRelativePath(assetFile, relativeFolder, '/');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
import org.apache.commons.lang.ArrayUtils;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Collection;
import java.util.HashSet;

/**
* @author Daniel Espendiller <daniel@espendiller.net>
Expand All @@ -23,7 +23,6 @@ public class AssetGoToDeclarationHandler implements GotoDeclarationHandler {
@Nullable
@Override
public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int i, Editor editor) {

if(!Symfony2ProjectComponent.isEnabled(psiElement)) {
return null;
}
Expand All @@ -33,9 +32,18 @@ public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int i, Edit
return null;
}

List<PsiElement> psiElements = new ArrayList<>();
Collection<PsiElement> psiElements = new HashSet<>();
for (VirtualFile virtualFile : TwigUtil.resolveAssetsFiles(psiElement.getProject(), psiElement.getText(), fileExtensionFilterIfValidTag)) {
psiElements.add(PsiManager.getInstance(psiElement.getProject()).findFile(virtualFile));
PsiElement target;
if(virtualFile.isDirectory()) {
target = PsiManager.getInstance(psiElement.getProject()).findDirectory(virtualFile);
} else {
target = PsiManager.getInstance(psiElement.getProject()).findFile(virtualFile);
}

if(target != null) {
psiElements.add(target);
}
}

return psiElements.toArray(new PsiElement[psiElements.size()]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
import com.intellij.codeInsight.lookup.LookupElementPresentation;
import com.intellij.openapi.project.Project;
import com.intellij.util.IconUtil;
import fr.adrienbrault.idea.symfony2plugin.asset.dic.AssetEnum;
import fr.adrienbrault.idea.symfony2plugin.asset.dic.AssetFile;
import fr.adrienbrault.idea.symfony2plugin.util.dict.ResourceFileInsertHandler;
import org.jetbrains.annotations.NotNull;

Expand Down
Loading