Skip to content

Commit 6100558

Browse files
authored
Merge pull request #1119 from Haehnchen/feature/1118-fix-optimize-assets
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
2 parents b6d3c01 + b3a96dc commit 6100558

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)