Skip to content

Commit 0e9dba8

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 0e9dba8

File tree

11 files changed

+214
-116
lines changed

11 files changed

+214
-116
lines changed

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

Lines changed: 87 additions & 6 deletions
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
import com.intellij.openapi.project.Project;
44
import com.intellij.openapi.vfs.VfsUtil;
@@ -9,15 +9,21 @@
99
import fr.adrienbrault.idea.symfony2plugin.Settings;
1010
import fr.adrienbrault.idea.symfony2plugin.util.SymfonyBundleUtil;
1111
import fr.adrienbrault.idea.symfony2plugin.util.dict.SymfonyBundle;
12+
import org.apache.commons.lang.StringUtils;
1213
import org.jetbrains.annotations.NotNull;
1314
import org.jetbrains.annotations.Nullable;
1415

1516
import java.util.*;
17+
import java.util.regex.Matcher;
18+
import java.util.regex.Pattern;
1619

1720
/**
1821
* @author Daniel Espendiller <daniel@espendiller.net>
1922
*/
2023
public class AssetDirectoryReader {
24+
25+
public static AssetDirectoryReader INSTANCE = new AssetDirectoryReader();
26+
2127
final private boolean includeBundleDir;
2228

2329
@NotNull
@@ -33,14 +39,15 @@ public AssetDirectoryReader(@NotNull String[] filterExtension, boolean includeBu
3339
}
3440

3541
@Nullable
36-
public static VirtualFile getProjectAssetRoot(@NotNull Project project) {
42+
private static VirtualFile getProjectAssetRoot(@NotNull Project project) {
3743
VirtualFile projectDirectory = project.getBaseDir();
3844
String webDirectoryName = Settings.getInstance(project).directoryToWeb;
3945
return VfsUtil.findRelativeFile(projectDirectory, webDirectoryName.split("/"));
4046
}
4147

42-
public List<AssetFile> getAssetFiles(@NotNull Project project) {
43-
List<AssetFile> files = new ArrayList<>();
48+
@NotNull
49+
public Collection<AssetFile> getAssetFiles(@NotNull Project project) {
50+
Collection<AssetFile> files = new ArrayList<>();
4451

4552
VirtualFile webDirectory = getProjectAssetRoot(project);
4653
if (null == webDirectory) {
@@ -62,7 +69,7 @@ public boolean visitFile(@NotNull VirtualFile virtualFile) {
6269
}
6370

6471
SymfonyBundleUtil symfonyBundleUtil = new SymfonyBundleUtil(PhpIndex.getInstance(project));
65-
for(final SymfonyBundle bundle : symfonyBundleUtil.getBundles()) {
72+
for(SymfonyBundle bundle : symfonyBundleUtil.getBundles()) {
6673
PsiDirectory bundleDirectory = bundle.getDirectory();
6774
if(null == bundleDirectory) {
6875
continue;
@@ -87,11 +94,85 @@ public boolean visitFile(@NotNull VirtualFile virtualFile) {
8794
return files;
8895
}
8996

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+
SymfonyBundle bundle = new SymfonyBundleUtil(PhpIndex.getInstance(project)).getBundle(filename.substring(1, i));
115+
if(bundle != null) {
116+
String substring = filename.substring(i + 1);
117+
VirtualFile relative = bundle.getRelative(substring);
118+
if(relative != null) {
119+
files.add(relative);
120+
}
121+
}
122+
}
123+
124+
return files;
125+
}
126+
127+
Collection<VirtualFile> files = new ArrayList<>();
128+
129+
VirtualFile webDirectory = getProjectAssetRoot(project);
130+
if (null == webDirectory) {
131+
return files;
132+
}
133+
134+
Matcher matcher = Pattern.compile("^(.*[/\\\\])\\*([.\\w+]*)$").matcher(assetName);
135+
if (!matcher.find()) {
136+
VirtualFile assetFile = VfsUtil.findRelativeFile(webDirectory, assetName.split("/"));
137+
if(assetFile != null) {
138+
files.add(assetFile);
139+
}
140+
} else {
141+
142+
String pathName = matcher.group(1);
143+
String fileExtension = matcher.group(2).length() > 0 ? matcher.group(2) : null;
144+
145+
pathName = StringUtils.stripEnd(pathName, "/");
146+
147+
if(fileExtension == null) {
148+
// @TODO: filter files
149+
// 'assets/js/*'
150+
VirtualFile assetFile = VfsUtil.findRelativeFile(webDirectory, pathName.split("/"));
151+
if(assetFile != null) {
152+
files.add(assetFile);
153+
}
154+
} else {
155+
// @TODO: filter files
156+
// 'assets/js/*.js'
157+
VirtualFile assetFile = VfsUtil.findRelativeFile(webDirectory, pathName.split("/"));
158+
if(assetFile != null) {
159+
files.add(assetFile);
160+
}
161+
}
162+
}
163+
164+
return files;
165+
}
166+
90167
private boolean isValidFile(@NotNull VirtualFile virtualFile) {
91-
if (this.filterExtension.size() == 0 || virtualFile.isDirectory()) {
168+
if (virtualFile.isDirectory()) {
92169
return false;
93170
}
94171

172+
if(filterExtension.size() == 0) {
173+
return true;
174+
}
175+
95176
String extension = virtualFile.getExtension();
96177
return extension != null && this.filterExtension.contains(extension);
97178
}

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/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

src/fr/adrienbrault/idea/symfony2plugin/asset/dic/AssetFile.java

Lines changed: 0 additions & 43 deletions
This file was deleted.

src/fr/adrienbrault/idea/symfony2plugin/asset/provider/AssetCompletionProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
import com.intellij.util.ProcessingContext;
1010
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
1111
import fr.adrienbrault.idea.symfony2plugin.asset.AssetLookupElement;
12-
import fr.adrienbrault.idea.symfony2plugin.asset.dic.AssetDirectoryReader;
13-
import fr.adrienbrault.idea.symfony2plugin.asset.dic.AssetFile;
12+
import fr.adrienbrault.idea.symfony2plugin.asset.AssetDirectoryReader;
13+
import fr.adrienbrault.idea.symfony2plugin.asset.AssetFile;
1414
import fr.adrienbrault.idea.symfony2plugin.twig.assets.TwigNamedAssetsServiceParser;
1515
import fr.adrienbrault.idea.symfony2plugin.util.service.ServiceXmlParserFactory;
1616
import org.jetbrains.annotations.NotNull;

src/fr/adrienbrault/idea/symfony2plugin/templating/TwigTemplateCompletionContributor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
import com.jetbrains.twig.elements.TwigElementTypes;
2121
import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons;
2222
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
23-
import fr.adrienbrault.idea.symfony2plugin.asset.dic.AssetDirectoryReader;
23+
import fr.adrienbrault.idea.symfony2plugin.asset.AssetDirectoryReader;
2424
import fr.adrienbrault.idea.symfony2plugin.asset.provider.AssetCompletionProvider;
2525
import fr.adrienbrault.idea.symfony2plugin.routing.RouteHelper;
2626
import fr.adrienbrault.idea.symfony2plugin.templating.completion.QuotedInsertionLookupElement;
@@ -258,6 +258,7 @@ public void addCompletions(@NotNull CompletionParameters parameters, ProcessingC
258258
// assets completion:
259259
// stylesheets and javascripts tags
260260

261+
// {{ asset('<caret>') }}
261262
extend(CompletionType.BASIC, TwigPattern.getAutocompletableAssetPattern(), new AssetCompletionProvider(
262263
new AssetDirectoryReader()
263264
));

src/fr/adrienbrault/idea/symfony2plugin/templating/completion/TwigHtmlCompletionContributor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
import com.intellij.util.ProcessingContext;
99
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
1010
import fr.adrienbrault.idea.symfony2plugin.asset.AssetLookupElement;
11-
import fr.adrienbrault.idea.symfony2plugin.asset.dic.AssetDirectoryReader;
12-
import fr.adrienbrault.idea.symfony2plugin.asset.dic.AssetFile;
11+
import fr.adrienbrault.idea.symfony2plugin.asset.AssetDirectoryReader;
12+
import fr.adrienbrault.idea.symfony2plugin.asset.AssetFile;
1313
import fr.adrienbrault.idea.symfony2plugin.routing.RouteHelper;
1414
import fr.adrienbrault.idea.symfony2plugin.routing.RouteLookupElement;
1515
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigHtmlCompletionUtil;

src/fr/adrienbrault/idea/symfony2plugin/templating/inspection/TwigAssetMissingInspection.java

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@
66
import com.intellij.psi.PsiElementVisitor;
77
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
88
import fr.adrienbrault.idea.symfony2plugin.templating.TwigPattern;
9-
import fr.adrienbrault.idea.symfony2plugin.asset.dic.AssetDirectoryReader;
10-
import fr.adrienbrault.idea.symfony2plugin.asset.dic.AssetFile;
119
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
10+
import org.apache.commons.lang.StringUtils;
1211
import org.jetbrains.annotations.NotNull;
1312

1413
/**
@@ -23,25 +22,34 @@ public PsiElementVisitor buildVisitor(final @NotNull ProblemsHolder holder, bool
2322
return super.buildVisitor(holder, isOnTheFly);
2423
}
2524

26-
return new PsiElementVisitor() {
27-
@Override
28-
public void visitElement(PsiElement element) {
29-
if(TwigPattern.getAutocompletableAssetPattern().accepts(element) && TwigUtil.isValidStringWithoutInterpolatedOrConcat(element)) {
30-
invoke(element, holder);
31-
}
25+
return new MyPsiElementVisitor(holder);
26+
}
27+
28+
private class MyPsiElementVisitor extends PsiElementVisitor {
29+
30+
private final ProblemsHolder holder;
3231

33-
super.visitElement(element);
32+
MyPsiElementVisitor(ProblemsHolder holder) {
33+
this.holder = holder;
34+
}
35+
36+
@Override
37+
public void visitElement(PsiElement element) {
38+
if(TwigPattern.getAutocompletableAssetPattern().accepts(element) && TwigUtil.isValidStringWithoutInterpolatedOrConcat(element)) {
39+
invoke(element, holder);
3440
}
35-
};
36-
}
3741

38-
private void invoke(@NotNull PsiElement element, @NotNull ProblemsHolder holder) {
39-
for (final AssetFile assetFile : new AssetDirectoryReader().getAssetFiles(element.getProject())) {
40-
if(assetFile.toString().equals(element.getText())) {
42+
super.visitElement(element);
43+
}
44+
45+
private void invoke(@NotNull PsiElement element, @NotNull ProblemsHolder holder) {
46+
String asset = element.getText();
47+
48+
if(StringUtils.isBlank(asset) || TwigUtil.resolveAssetsFiles(element.getProject(), asset).size() > 0) {
4149
return;
4250
}
43-
}
4451

45-
holder.registerProblem(element, "Missing asset");
52+
holder.registerProblem(element, "Missing asset");
53+
}
4654
}
4755
}

0 commit comments

Comments
 (0)