Skip to content

Commit b3a96dc

Browse files
committed
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
1 parent b6d3c01 commit b3a96dc

File tree

13 files changed

+341
-213
lines changed

13 files changed

+341
-213
lines changed
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
package fr.adrienbrault.idea.symfony2plugin.asset;
2+
3+
import com.intellij.openapi.project.Project;
4+
import com.intellij.openapi.vfs.VfsUtil;
5+
import com.intellij.openapi.vfs.VirtualFile;
6+
import com.intellij.openapi.vfs.VirtualFileVisitor;
7+
import com.intellij.psi.PsiDirectory;
8+
import com.jetbrains.php.PhpIndex;
9+
import fr.adrienbrault.idea.symfony2plugin.Settings;
10+
import fr.adrienbrault.idea.symfony2plugin.util.SymfonyBundleUtil;
11+
import fr.adrienbrault.idea.symfony2plugin.util.dict.SymfonyBundle;
12+
import org.apache.commons.lang.StringUtils;
13+
import org.jetbrains.annotations.NotNull;
14+
import org.jetbrains.annotations.Nullable;
15+
16+
import java.util.*;
17+
import java.util.regex.Matcher;
18+
import java.util.regex.Pattern;
19+
20+
/**
21+
* @author Daniel Espendiller <daniel@espendiller.net>
22+
*/
23+
public class AssetDirectoryReader {
24+
25+
public static AssetDirectoryReader INSTANCE = new AssetDirectoryReader();
26+
27+
final private boolean includeBundleDir;
28+
29+
@NotNull
30+
final private Collection<String> filterExtension = new HashSet<>();
31+
32+
public AssetDirectoryReader() {
33+
includeBundleDir = false;
34+
}
35+
36+
public AssetDirectoryReader(@NotNull String[] filterExtension, boolean includeBundleDir) {
37+
this.includeBundleDir = includeBundleDir;
38+
this.filterExtension.addAll(Arrays.asList(filterExtension));
39+
}
40+
41+
@Nullable
42+
private static VirtualFile getProjectAssetRoot(@NotNull Project project) {
43+
VirtualFile projectDirectory = project.getBaseDir();
44+
String webDirectoryName = Settings.getInstance(project).directoryToWeb;
45+
return VfsUtil.findRelativeFile(projectDirectory, webDirectoryName.split("/"));
46+
}
47+
48+
@NotNull
49+
public Collection<AssetFile> getAssetFiles(@NotNull Project project) {
50+
Collection<AssetFile> files = new ArrayList<>();
51+
52+
VirtualFile webDirectory = getProjectAssetRoot(project);
53+
if (null == webDirectory) {
54+
return files;
55+
}
56+
57+
VfsUtil.visitChildrenRecursively(webDirectory, new VirtualFileVisitor() {
58+
@Override
59+
public boolean visitFile(@NotNull VirtualFile virtualFile) {
60+
if(isValidFile(virtualFile)) {
61+
files.add(new AssetFile(virtualFile, AssetEnum.Position.Web, webDirectory));
62+
}
63+
return super.visitFile(virtualFile);
64+
}
65+
});
66+
67+
if(!this.includeBundleDir) {
68+
return files;
69+
}
70+
71+
SymfonyBundleUtil symfonyBundleUtil = new SymfonyBundleUtil(PhpIndex.getInstance(project));
72+
for(SymfonyBundle bundle : symfonyBundleUtil.getBundles()) {
73+
PsiDirectory bundleDirectory = bundle.getDirectory();
74+
if(null == bundleDirectory) {
75+
continue;
76+
}
77+
78+
VirtualFile bundleDirectoryVirtual = bundleDirectory.getVirtualFile();
79+
VirtualFile resourceDirectory = VfsUtil.findRelativeFile(bundleDirectoryVirtual, "Resources");
80+
81+
if (null != resourceDirectory) {
82+
VfsUtil.visitChildrenRecursively(resourceDirectory, new VirtualFileVisitor() {
83+
@Override
84+
public boolean visitFile(@NotNull VirtualFile virtualFile) {
85+
if(isValidFile(virtualFile)) {
86+
files.add(new AssetFile(virtualFile, AssetEnum.Position.Bundle, bundleDirectoryVirtual, '@' + bundle.getName() + "/"));
87+
}
88+
return super.visitFile(virtualFile);
89+
}
90+
});
91+
}
92+
}
93+
94+
return files;
95+
}
96+
97+
/**
98+
* '@SampleBundle/Resources/public/js/*'
99+
* 'assets/js/*'
100+
* 'assets/js/*.js'
101+
*/
102+
@NotNull
103+
public Collection<VirtualFile> resolveAssetFile(@NotNull Project project, @NotNull String filename) {
104+
String assetName = StringUtils.stripStart(filename.replace("\\", "/").replaceAll("/+", "/"), "/");
105+
106+
// '@SampleBundle/Resources/public/js/foo,js'
107+
// TODO: '@SampleBundle/Resources/public/js/*'
108+
// TODO: '@SampleBundle/Resources/public/js/*.js'
109+
if(filename.startsWith("@")) {
110+
Collection<VirtualFile> files = new ArrayList<>();
111+
112+
int i = filename.indexOf("/");
113+
if(i > 0) {
114+
String relativeFilename = filename.substring(1, i);
115+
SymfonyBundle bundle = new SymfonyBundleUtil(PhpIndex.getInstance(project)).getBundle(relativeFilename);
116+
if(bundle != null) {
117+
String assetPath = filename.substring(i + 1);
118+
119+
Matcher matcher = Pattern.compile("^(.*[/\\\\])\\*([.\\w+]*)$").matcher(assetPath);
120+
if (!matcher.find()) {
121+
VirtualFile relative = bundle.getRelative(assetPath);
122+
if(relative != null) {
123+
files.add(relative);
124+
}
125+
} else {
126+
// "/*"
127+
// "/*.js"
128+
PsiDirectory directory = bundle.getDirectory();
129+
if(directory != null) {
130+
files.addAll(collectWildcardDirectories(matcher, directory.getVirtualFile()));
131+
}
132+
}
133+
}
134+
}
135+
136+
return files;
137+
}
138+
139+
Collection<VirtualFile> files = new ArrayList<>();
140+
141+
VirtualFile webDirectory = getProjectAssetRoot(project);
142+
if (null == webDirectory) {
143+
return files;
144+
}
145+
146+
Matcher matcher = Pattern.compile("^(.*[/\\\\])\\*([.\\w+]*)$").matcher(assetName);
147+
if (!matcher.find()) {
148+
VirtualFile assetFile = VfsUtil.findRelativeFile(webDirectory, assetName.split("/"));
149+
if(assetFile != null) {
150+
files.add(assetFile);
151+
}
152+
} else {
153+
// "/*"
154+
// "/*.js"
155+
files.addAll(collectWildcardDirectories(matcher, webDirectory));
156+
}
157+
158+
return files;
159+
}
160+
161+
private Collection<VirtualFile> collectWildcardDirectories(@NotNull Matcher matcher, @NotNull VirtualFile directory) {
162+
Collection<VirtualFile> files = new HashSet<>();
163+
164+
String pathName = matcher.group(1);
165+
String fileExtension = matcher.group(2).length() > 0 ? matcher.group(2) : null;
166+
167+
pathName = StringUtils.stripEnd(pathName, "/");
168+
169+
if(fileExtension == null) {
170+
// @TODO: filter files
171+
// 'assets/js/*'
172+
VirtualFile assetFile = VfsUtil.findRelativeFile(directory, pathName.split("/"));
173+
if(assetFile != null) {
174+
files.add(assetFile);
175+
}
176+
} else {
177+
// @TODO: filter files
178+
// 'assets/js/*.js'
179+
VirtualFile assetFile = VfsUtil.findRelativeFile(directory, pathName.split("/"));
180+
if(assetFile != null) {
181+
files.add(assetFile);
182+
}
183+
}
184+
185+
return files;
186+
}
187+
188+
private boolean isValidFile(@NotNull VirtualFile virtualFile) {
189+
if (virtualFile.isDirectory()) {
190+
return false;
191+
}
192+
193+
if(filterExtension.size() == 0) {
194+
return true;
195+
}
196+
197+
String extension = virtualFile.getExtension();
198+
return extension != null && this.filterExtension.contains(extension);
199+
}
200+
}

src/fr/adrienbrault/idea/symfony2plugin/asset/dic/AssetEnum.java renamed to src/fr/adrienbrault/idea/symfony2plugin/asset/AssetEnum.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package fr.adrienbrault.idea.symfony2plugin.asset.dic;
1+
package fr.adrienbrault.idea.symfony2plugin.asset;
22

33
/**
44
* @author Daniel Espendiller <daniel@espendiller.net>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package fr.adrienbrault.idea.symfony2plugin.asset;
2+
3+
import com.intellij.openapi.vfs.VfsUtil;
4+
import com.intellij.openapi.vfs.VirtualFile;
5+
import org.jetbrains.annotations.NotNull;
6+
7+
/**
8+
* @author Daniel Espendiller <daniel@espendiller.net>
9+
*/
10+
public class AssetFile {
11+
@NotNull
12+
private VirtualFile assetFile;
13+
14+
@NotNull
15+
private AssetEnum.Position assetPosition;
16+
17+
@NotNull
18+
private VirtualFile relativeFolder;
19+
20+
private String prefix = "";
21+
22+
public AssetFile(@NotNull VirtualFile assetFile, @NotNull AssetEnum.Position assetPosition, @NotNull VirtualFile relativeFolder, @NotNull String prefix) {
23+
this(assetFile, assetPosition, relativeFolder);
24+
this.prefix = prefix;
25+
}
26+
27+
public AssetFile(@NotNull VirtualFile assetFile, @NotNull AssetEnum.Position assetPosition, @NotNull VirtualFile relativeFolder) {
28+
this.assetFile = assetFile;
29+
this.assetPosition = assetPosition;
30+
this.relativeFolder = relativeFolder;
31+
}
32+
33+
@NotNull
34+
public VirtualFile getFile() {
35+
return assetFile;
36+
}
37+
38+
@NotNull
39+
public AssetEnum.Position getAssetPosition() {
40+
return assetPosition;
41+
}
42+
43+
public String toString() {
44+
return this.prefix + VfsUtil.getRelativePath(assetFile, relativeFolder, '/');
45+
}
46+
}

src/fr/adrienbrault/idea/symfony2plugin/asset/AssetGoToDeclarationHandler.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
import org.apache.commons.lang.ArrayUtils;
1313
import org.jetbrains.annotations.Nullable;
1414

15-
import java.util.ArrayList;
16-
import java.util.List;
15+
import java.util.Collection;
16+
import java.util.HashSet;
1717

1818
/**
1919
* @author Daniel Espendiller <daniel@espendiller.net>
@@ -23,7 +23,6 @@ public class AssetGoToDeclarationHandler implements GotoDeclarationHandler {
2323
@Nullable
2424
@Override
2525
public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int i, Editor editor) {
26-
2726
if(!Symfony2ProjectComponent.isEnabled(psiElement)) {
2827
return null;
2928
}
@@ -33,9 +32,18 @@ public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int i, Edit
3332
return null;
3433
}
3534

36-
List<PsiElement> psiElements = new ArrayList<>();
35+
Collection<PsiElement> psiElements = new HashSet<>();
3736
for (VirtualFile virtualFile : TwigUtil.resolveAssetsFiles(psiElement.getProject(), psiElement.getText(), fileExtensionFilterIfValidTag)) {
38-
psiElements.add(PsiManager.getInstance(psiElement.getProject()).findFile(virtualFile));
37+
PsiElement target;
38+
if(virtualFile.isDirectory()) {
39+
target = PsiManager.getInstance(psiElement.getProject()).findDirectory(virtualFile);
40+
} else {
41+
target = PsiManager.getInstance(psiElement.getProject()).findFile(virtualFile);
42+
}
43+
44+
if(target != null) {
45+
psiElements.add(target);
46+
}
3947
}
4048

4149
return psiElements.toArray(new PsiElement[psiElements.size()]);

src/fr/adrienbrault/idea/symfony2plugin/asset/AssetLookupElement.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
import com.intellij.codeInsight.lookup.LookupElementPresentation;
77
import com.intellij.openapi.project.Project;
88
import com.intellij.util.IconUtil;
9-
import fr.adrienbrault.idea.symfony2plugin.asset.dic.AssetEnum;
10-
import fr.adrienbrault.idea.symfony2plugin.asset.dic.AssetFile;
119
import fr.adrienbrault.idea.symfony2plugin.util.dict.ResourceFileInsertHandler;
1210
import org.jetbrains.annotations.NotNull;
1311

0 commit comments

Comments
 (0)