Skip to content

Commit 72a8c72

Browse files
authored
Merge pull request #1939 from Haehnchen/feature/resources-file
support file and directory resolving for resources
2 parents 0bc89cc + 1b14f59 commit 72a8c72

File tree

1 file changed

+172
-23
lines changed

1 file changed

+172
-23
lines changed

src/main/java/fr/adrienbrault/idea/symfony2plugin/util/resource/FileResourceUtil.java

Lines changed: 172 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,34 @@
44
import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder;
55
import com.intellij.openapi.project.Project;
66
import com.intellij.openapi.util.NotNullLazyValue;
7+
import com.intellij.openapi.util.Pair;
78
import com.intellij.openapi.vfs.VfsUtil;
89
import com.intellij.openapi.vfs.VirtualFile;
910
import com.intellij.psi.PsiDirectory;
1011
import com.intellij.psi.PsiElement;
1112
import com.intellij.psi.PsiFile;
1213
import com.intellij.psi.PsiManager;
1314
import com.intellij.psi.search.GlobalSearchScope;
15+
import com.intellij.psi.util.CachedValueProvider;
16+
import com.intellij.psi.util.CachedValuesManager;
1417
import com.intellij.util.PlatformIcons;
1518
import com.intellij.util.indexing.FileBasedIndex;
1619
import com.jetbrains.php.PhpIcons;
20+
import fr.adrienbrault.idea.symfony2plugin.stubs.cache.FileIndexCaches;
1721
import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.FileResourcesIndex;
18-
import fr.adrienbrault.idea.symfony2plugin.util.FileResourceVisitorUtil;
19-
import fr.adrienbrault.idea.symfony2plugin.util.PhpIndexUtil;
20-
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
21-
import fr.adrienbrault.idea.symfony2plugin.util.SymfonyBundleUtil;
22+
import fr.adrienbrault.idea.symfony2plugin.util.*;
2223
import fr.adrienbrault.idea.symfony2plugin.util.dict.SymfonyBundle;
2324
import org.apache.commons.lang.StringUtils;
2425
import org.jetbrains.annotations.NotNull;
2526
import org.jetbrains.annotations.Nullable;
2627

28+
import java.io.File;
29+
import java.io.IOException;
30+
import java.nio.file.*;
31+
import java.nio.file.attribute.BasicFileAttributes;
2732
import java.util.*;
2833
import java.util.function.Supplier;
34+
import java.util.stream.Collectors;
2935

3036
/**
3137
* @author Daniel Espendiller <daniel@espendiller.net>
@@ -49,14 +55,84 @@ public static Collection<VirtualFile> getFileResourceRefers(@NotNull Project pro
4955
* Search for files refers to given file
5056
*/
5157
@NotNull
52-
public static Collection<VirtualFile> getFileResourceRefers(@NotNull Project project, @NotNull String bundleLocateName) {
58+
private static Collection<VirtualFile> getFileResourceRefers(@NotNull Project project, @NotNull String bundleLocateName) {
5359
return FileBasedIndex.getInstance().getContainingFiles(
5460
FileResourcesIndex.KEY,
5561
bundleLocateName,
5662
GlobalSearchScope.allScope(project)
5763
);
5864
}
5965

66+
/**
67+
* Search for files refers to given file
68+
*/
69+
public static boolean hasFileResources(@NotNull Project project, @NotNull PsiFile psiFile) {
70+
return CachedValuesManager.getCachedValue(
71+
psiFile,
72+
() -> {
73+
VirtualFile virtualFile = psiFile.getVirtualFile();
74+
if (virtualFile == null) {
75+
return CachedValueProvider.Result.create(Boolean.FALSE, FileIndexCaches.getModificationTrackerForIndexId(project, FileResourcesIndex.KEY));
76+
}
77+
78+
Set<String> collect = FileBasedIndex.getInstance().getAllKeys(FileResourcesIndex.KEY, project)
79+
.stream()
80+
.filter(s -> !s.startsWith("@"))
81+
.collect(Collectors.toSet());
82+
83+
for (String s : collect) {
84+
for (VirtualFile containingFile : FileBasedIndex.getInstance().getContainingFiles(FileResourcesIndex.KEY, s, GlobalSearchScope.allScope(project))) {
85+
VirtualFile directory = containingFile.getParent();
86+
if (directory == null) {
87+
continue;
88+
}
89+
90+
VirtualFile relativeFile = VfsUtil.findRelativeFile(directory, s.replace("\\", "/").split("/"));
91+
if (relativeFile != null) {
92+
String relativePath = VfsUtil.getRelativePath(virtualFile, relativeFile);
93+
if (relativePath != null) {
94+
return CachedValueProvider.Result.create(Boolean.TRUE, FileIndexCaches.getModificationTrackerForIndexId(project, FileResourcesIndex.KEY));
95+
}
96+
}
97+
}
98+
}
99+
100+
return CachedValueProvider.Result.create(Boolean.FALSE, FileIndexCaches.getModificationTrackerForIndexId(project, FileResourcesIndex.KEY));
101+
}
102+
);
103+
}
104+
105+
/**
106+
* Search for files refers to given file
107+
*/
108+
@NotNull
109+
public static Collection<Pair<VirtualFile, String>> getFileResources(@NotNull Project project, @NotNull VirtualFile virtualFile) {
110+
Set<String> collect = FileBasedIndex.getInstance().getAllKeys(FileResourcesIndex.KEY, project)
111+
.stream()
112+
.filter(s -> !s.startsWith("@"))
113+
.collect(Collectors.toSet());
114+
115+
Collection<Pair<VirtualFile, String>> files = new ArrayList<>();
116+
for (String s : collect) {
117+
for (VirtualFile containingFile : FileBasedIndex.getInstance().getContainingFiles(FileResourcesIndex.KEY, s, GlobalSearchScope.allScope(project))) {
118+
VirtualFile directory = containingFile.getParent();
119+
if (directory == null) {
120+
continue;
121+
}
122+
123+
VirtualFile relativeFile = VfsUtil.findRelativeFile(directory, s.replace("\\", "/").split("/"));
124+
if (relativeFile != null) {
125+
String relativePath = VfsUtil.getRelativePath(virtualFile, relativeFile);
126+
if (relativePath != null) {
127+
files.add(Pair.create(containingFile, s));
128+
}
129+
}
130+
}
131+
}
132+
133+
return files;
134+
}
135+
60136
@Nullable
61137
public static String getBundleLocateName(@NotNull Project project, @NotNull VirtualFile virtualFile) {
62138
SymfonyBundle containingBundle = new SymfonyBundleUtil(project).getContainingBundle(virtualFile);
@@ -104,19 +180,28 @@ public static RelatedItemLineMarkerInfo<PsiElement> getFileImplementsLineMarker(
104180
}
105181

106182
String bundleLocateName = FileResourceUtil.getBundleLocateName(project, virtualFile);
107-
if(bundleLocateName == null) {
108-
return null;
109-
}
183+
if(bundleLocateName != null) {
184+
if(FileResourceUtil.getFileResourceRefers(project, bundleLocateName).size() == 0) {
185+
return null;
186+
}
110187

111-
if(FileResourceUtil.getFileResourceRefers(project, bundleLocateName).size() == 0) {
112-
return null;
113-
}
188+
NavigationGutterIconBuilder<PsiElement> builder = NavigationGutterIconBuilder.create(PhpIcons.IMPLEMENTS)
189+
.setTargets(NotNullLazyValue.lazy(new FileResourceBundleNotNullLazyValue(project, bundleLocateName)))
190+
.setTooltipText("Navigate to resource");
114191

115-
NavigationGutterIconBuilder<PsiElement> builder = NavigationGutterIconBuilder.create(PhpIcons.IMPLEMENTS)
116-
.setTargets(NotNullLazyValue.lazy(new FileResourceUtil.FileResourceNotNullLazyValue(project, bundleLocateName)))
117-
.setTooltipText("Navigate to resource");
192+
return builder.createLineMarkerInfo(psiFile);
193+
} else {
194+
if (hasFileResources(project, psiFile)) {
195+
NavigationGutterIconBuilder<PsiElement> builder = NavigationGutterIconBuilder.create(PhpIcons.IMPLEMENTS);
196+
builder.setTargets(NotNullLazyValue.lazy(new FileResourceNotNullLazyValue(project, virtualFile)));
118197

119-
return builder.createLineMarkerInfo(psiFile);
198+
builder.setTooltipText("Navigate to resource");
199+
200+
return builder.createLineMarkerInfo(psiFile);
201+
}
202+
}
203+
204+
return null;
120205
}
121206

122207
/**
@@ -154,23 +239,23 @@ public static RelatedItemLineMarkerInfo<PsiElement> getFileImplementsLineMarkerI
154239
}
155240

156241
NavigationGutterIconBuilder<PsiElement> builder = NavigationGutterIconBuilder.create(PlatformIcons.ANNOTATION_TYPE_ICON)
157-
.setTargets(NotNullLazyValue.lazy(new FileResourceUtil.FileResourceNotNullLazyValue(project, names)))
242+
.setTargets(NotNullLazyValue.lazy(new FileResourceBundleNotNullLazyValue(project, names)))
158243
.setTooltipText("Symfony: <a href=\"https://symfony.com/doc/current/routing.html#creating-routes-as-annotations\">Annotation Routing</a>");
159244

160245
return builder.createLineMarkerInfo(psiFile);
161246
}
162247

163-
private static class FileResourceNotNullLazyValue implements Supplier<Collection<? extends PsiElement>> {
248+
private static class FileResourceBundleNotNullLazyValue implements Supplier<Collection<? extends PsiElement>> {
164249

165250
private final Collection<String> resources;
166251
private final Project project;
167252

168-
public FileResourceNotNullLazyValue(@NotNull Project project, @NotNull Collection<String> resource) {
253+
public FileResourceBundleNotNullLazyValue(@NotNull Project project, @NotNull Collection<String> resource) {
169254
this.resources = resource;
170255
this.project = project;
171256
}
172257

173-
public FileResourceNotNullLazyValue(@NotNull Project project, @NotNull String resource) {
258+
public FileResourceBundleNotNullLazyValue(@NotNull Project project, @NotNull String resource) {
174259
this.resources = Collections.singleton(resource);
175260
this.project = project;
176261
}
@@ -264,11 +349,75 @@ public static Collection<PsiFile> getFileResourceTargetsInDirectoryScope(@NotNul
264349
return Collections.emptyList();
265350
}
266351

267-
PsiFile targetFile = PsiElementUtils.virtualFileToPsiFile(psiFile.getProject(), relativeFile);
268-
if(targetFile == null) {
269-
return Collections.emptyList();
352+
Set<PsiFile> psiFiles = new HashSet<>();
353+
if (relativeFile.isDirectory()) {
354+
String path = relativeFile.getPath();
355+
final PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:/**/*.php");
356+
357+
Set<String> files = new HashSet<>();
358+
try {
359+
Files.walkFileTree(Paths.get(path), new SimpleFileVisitor<>() {
360+
@Override
361+
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
362+
if (pathMatcher.matches(path)) {
363+
files.add(path.toString());
364+
}
365+
return files.size() < 200 ? FileVisitResult.CONTINUE : FileVisitResult.TERMINATE;
366+
}
367+
368+
@Override
369+
public FileVisitResult visitFileFailed(Path file, IOException exc) {
370+
return FileVisitResult.CONTINUE;
371+
}
372+
});
373+
} catch (IOException ignored) {
374+
}
375+
376+
Set<PsiFile> collect = files.stream()
377+
.map(s -> VfsUtil.findFileByIoFile(new File(s), false))
378+
.filter(Objects::nonNull)
379+
.map(virtualFile -> PsiElementUtils.virtualFileToPsiFile(psiFile.getProject(), virtualFile))
380+
.filter(Objects::nonNull)
381+
.collect(Collectors.toSet());
382+
383+
psiFiles.addAll(collect);
384+
} else {
385+
PsiFile targetFile = PsiElementUtils.virtualFileToPsiFile(psiFile.getProject(), relativeFile);
386+
if(targetFile != null) {
387+
psiFiles.add(targetFile);
388+
}
389+
}
390+
391+
return psiFiles;
392+
}
393+
394+
private static class FileResourceNotNullLazyValue implements Supplier<Collection<? extends PsiElement>> {
395+
private final Project project;
396+
private final VirtualFile virtualFile;
397+
398+
public FileResourceNotNullLazyValue(Project project, VirtualFile virtualFile) {
399+
this.project = project;
400+
this.virtualFile = virtualFile;
270401
}
271402

272-
return Collections.singletonList(targetFile);
403+
@Override
404+
public Collection<? extends PsiElement> get() {
405+
Collection<PsiElement> psiElements = new HashSet<>();
406+
407+
for (Pair<VirtualFile, String> pair : getFileResources(project, virtualFile)) {
408+
PsiFile psiFile1 = PsiElementUtils.virtualFileToPsiFile(project, pair.getFirst());
409+
if (psiFile1 == null) {
410+
continue;
411+
}
412+
413+
FileResourceVisitorUtil.visitFile(psiFile1, fileResourceConsumer -> {
414+
if (fileResourceConsumer.getResource().equalsIgnoreCase(pair.getSecond())) {
415+
psiElements.add(fileResourceConsumer.getPsiElement());
416+
}
417+
});
418+
}
419+
420+
return psiElements;
421+
}
273422
}
274423
}

0 commit comments

Comments
 (0)