44import com .intellij .codeInsight .navigation .NavigationGutterIconBuilder ;
55import com .intellij .openapi .project .Project ;
66import com .intellij .openapi .util .NotNullLazyValue ;
7+ import com .intellij .openapi .util .Pair ;
78import com .intellij .openapi .vfs .VfsUtil ;
89import com .intellij .openapi .vfs .VirtualFile ;
910import com .intellij .psi .PsiDirectory ;
1011import com .intellij .psi .PsiElement ;
1112import com .intellij .psi .PsiFile ;
1213import com .intellij .psi .PsiManager ;
1314import com .intellij .psi .search .GlobalSearchScope ;
15+ import com .intellij .psi .util .CachedValueProvider ;
16+ import com .intellij .psi .util .CachedValuesManager ;
1417import com .intellij .util .PlatformIcons ;
1518import com .intellij .util .indexing .FileBasedIndex ;
1619import com .jetbrains .php .PhpIcons ;
20+ import fr .adrienbrault .idea .symfony2plugin .stubs .cache .FileIndexCaches ;
1721import 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 .*;
2223import fr .adrienbrault .idea .symfony2plugin .util .dict .SymfonyBundle ;
2324import org .apache .commons .lang .StringUtils ;
2425import org .jetbrains .annotations .NotNull ;
2526import 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 ;
2732import java .util .*;
2833import 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