1010import com .intellij .psi .PsiDirectory ;
1111import com .intellij .psi .PsiElement ;
1212import com .intellij .psi .PsiFile ;
13- import com .intellij .psi .PsiManager ;
1413import com .intellij .psi .search .GlobalSearchScope ;
1514import com .intellij .psi .util .CachedValueProvider ;
1615import com .intellij .psi .util .CachedValuesManager ;
1716import com .intellij .util .PlatformIcons ;
1817import com .intellij .util .indexing .FileBasedIndex ;
1918import com .jetbrains .php .PhpIcons ;
2019import fr .adrienbrault .idea .symfony2plugin .stubs .cache .FileIndexCaches ;
20+ import fr .adrienbrault .idea .symfony2plugin .stubs .dict .FileResource ;
21+ import fr .adrienbrault .idea .symfony2plugin .stubs .dict .FileResourceContextTypeEnum ;
2122import fr .adrienbrault .idea .symfony2plugin .stubs .indexes .FileResourcesIndex ;
2223import fr .adrienbrault .idea .symfony2plugin .util .FileResourceVisitorUtil ;
2324import fr .adrienbrault .idea .symfony2plugin .util .PhpIndexUtil ;
3334import java .nio .file .*;
3435import java .nio .file .attribute .BasicFileAttributes ;
3536import java .util .*;
37+ import java .util .function .Function ;
3638import java .util .function .Supplier ;
3739import java .util .regex .PatternSyntaxException ;
3840import java .util .stream .Collectors ;
@@ -48,31 +50,6 @@ public class FileResourceUtil {
4850 */
4951 private static final String [] GLOB_DETECTION_CHARS = {"*" , "?" , "{" , "[" };
5052
51- /**
52- * Search for files refers to given file
53- */
54- @ NotNull
55- public static Collection <VirtualFile > getFileResourceRefers (@ NotNull Project project , @ NotNull VirtualFile virtualFile ) {
56- String bundleLocateName = getBundleLocateName (project , virtualFile );
57- if (bundleLocateName == null ) {
58- return Collections .emptyList ();
59- }
60-
61- return getFileResourceRefers (project , bundleLocateName );
62- }
63-
64- /**
65- * Search for files refers to given file
66- */
67- @ NotNull
68- private static Collection <VirtualFile > getFileResourceRefers (@ NotNull Project project , @ NotNull String bundleLocateName ) {
69- return FileBasedIndex .getInstance ().getContainingFiles (
70- FileResourcesIndex .KEY ,
71- bundleLocateName ,
72- GlobalSearchScope .allScope (project )
73- );
74- }
75-
7653 /**
7754 * Search for files refers to given file
7855 */
@@ -85,66 +62,14 @@ public static boolean hasFileResources(@NotNull Project project, @NotNull PsiFil
8562 return CachedValueProvider .Result .create (Boolean .FALSE , FileIndexCaches .getModificationTrackerForIndexId (project , FileResourcesIndex .KEY ));
8663 }
8764
88- Set < String > resources = new HashSet <>( FileBasedIndex . getInstance (). getAllKeys ( FileResourcesIndex . KEY , project )) ;
65+ final Boolean [] aFalse = { Boolean . FALSE } ;
8966
90- for (String resource : resources ) {
91- for (VirtualFile containingFile : FileBasedIndex .getInstance ().getContainingFiles (FileResourcesIndex .KEY , resource , GlobalSearchScope .allScope (project ))) {
92- VirtualFile directory = containingFile .getParent ();
93- if (directory == null ) {
94- continue ;
95- }
96-
97- String resourceResolved = resource ;
98-
99- if (resource .startsWith ("@" )) {
100- String replace = resource .replace ("\\ " , "/" );
101- int i = replace .indexOf ("/" );
102- if (i > 2 ) {
103- String substring = resource .substring (1 , i );
104- Collection <SymfonyBundle > bundle = new SymfonyBundleUtil (project ).getBundle (substring );
105-
106- for (SymfonyBundle symfonyBundle : bundle ) {
107- PsiDirectory directory1 = symfonyBundle .getDirectory ();
108- if (directory1 == null ) {
109- continue ;
110- }
111-
112- String substring1 = resource .substring (i );
113- String path = directory1 .getVirtualFile ().getPath ();
114- resourceResolved = path + substring1 ;
115-
116- break ;
117- }
118- }
119- }
120-
121- if (Arrays .stream (GLOB_DETECTION_CHARS ).anyMatch (resource ::contains )) {
122- String path = directory .getPath ();
123-
124- // nested types not support by java glob implementation so just catch the exception: "../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php,Service/{IspConfiguration,DataCollection}}"
125- try {
126- String s1 = Paths .get (path + File .separatorChar + StringUtils .stripStart (resourceResolved , "\\ /" )).normalize ().toString ();
127- String syntaxAndPattern = "glob:" + s1 ;
128- if (FileSystems .getDefault ().getPathMatcher (syntaxAndPattern ).matches (Paths .get (virtualFile .getPath ()))) {
129- return CachedValueProvider .Result .create (Boolean .TRUE , FileIndexCaches .getModificationTrackerForIndexId (project , FileResourcesIndex .KEY ));
130- }
131- } catch (PatternSyntaxException | InvalidPathException ignored ) {
132- }
133-
134- continue ;
135- }
136-
137- VirtualFile relativeFile = VfsUtil .findRelativeFile (directory , resourceResolved .replace ("\\ " , "/" ).split ("/" ));
138- if (relativeFile != null ) {
139- String relativePath = VfsUtil .getRelativePath (virtualFile , relativeFile );
140- if (relativePath != null ) {
141- return CachedValueProvider .Result .create (Boolean .TRUE , FileIndexCaches .getModificationTrackerForIndexId (project , FileResourcesIndex .KEY ));
142- }
143- }
144- }
145- }
67+ visitFileResources (project , virtualFile , pair -> {
68+ aFalse [0 ] = Boolean .TRUE ;
69+ return true ;
70+ });
14671
147- return CachedValueProvider .Result .create (Boolean . FALSE , FileIndexCaches .getModificationTrackerForIndexId (project , FileResourcesIndex .KEY ));
72+ return CachedValueProvider .Result .create (aFalse [ 0 ] , FileIndexCaches .getModificationTrackerForIndexId (project , FileResourcesIndex .KEY ));
14873 }
14974 );
15075 }
@@ -154,11 +79,32 @@ public static boolean hasFileResources(@NotNull Project project, @NotNull PsiFil
15479 */
15580 @ NotNull
15681 public static Collection <Pair <VirtualFile , String >> getFileResources (@ NotNull Project project , @ NotNull VirtualFile virtualFile ) {
157- Set <String > resources = new HashSet <>(FileBasedIndex .getInstance ().getAllKeys (FileResourcesIndex .KEY , project ));
158-
15982 Collection <Pair <VirtualFile , String >> files = new ArrayList <>();
160- for (String resource : resources ) {
161- for (VirtualFile containingFile : FileBasedIndex .getInstance ().getContainingFiles (FileResourcesIndex .KEY , resource , GlobalSearchScope .allScope (project ))) {
83+
84+ visitFileResources (project , virtualFile , pair -> {
85+ files .add (Pair .create (pair .getFirst (), pair .getSecond ()));
86+ return false ;
87+ });
88+
89+ return files ;
90+ }
91+
92+ /**
93+ * Search for files refers to given file
94+ */
95+ public static void visitFileResources (@ NotNull Project project , @ NotNull VirtualFile virtualFile , @ NotNull Function <Pair <VirtualFile , String >, Boolean > consumer ) {
96+ Set <VirtualFile > files = new HashSet <>();
97+ for (String resource : FileBasedIndex .getInstance ().getAllKeys (FileResourcesIndex .KEY , project )) {
98+ files .addAll (FileBasedIndex .getInstance ().getContainingFiles (FileResourcesIndex .KEY , resource , GlobalSearchScope .allScope (project )));
99+ }
100+
101+ for (VirtualFile containingFile : files ) {
102+ for (FileResource fileResourceContext : FileBasedIndex .getInstance ().getFileData (FileResourcesIndex .KEY , containingFile , project ).values ()) {
103+ String resource = fileResourceContext .getResource ();
104+ if (resource == null ) {
105+ continue ;
106+ }
107+
162108 VirtualFile directory = containingFile .getParent ();
163109 if (directory == null ) {
164110 continue ;
@@ -169,7 +115,14 @@ public static Collection<Pair<VirtualFile, String>> getFileResources(@NotNull Pr
169115 if (resource .startsWith ("@" )) {
170116 String replace = resource .replace ("\\ " , "/" );
171117 int i = replace .indexOf ("/" );
172- if (i > 2 ) {
118+
119+ boolean resolved = false ;
120+
121+ if (i > 2 || i == -1 ) {
122+ if (i == -1 ) {
123+ i = resource .length ();
124+ }
125+
173126 String substring = resource .substring (1 , i );
174127 Collection <SymfonyBundle > bundle = new SymfonyBundleUtil (project ).getBundle (substring );
175128
@@ -179,15 +132,22 @@ public static Collection<Pair<VirtualFile, String>> getFileResources(@NotNull Pr
179132 continue ;
180133 }
181134
182- String substring1 = resource .substring (i );
183- String path = directory1 .getVirtualFile ().getPath ();
184- resourceResolved = path + substring1 ;
135+ resourceResolved = resource .substring (replace .contains ("/" ) ? i + 1 : replace .length ());
136+ directory = directory1 .getVirtualFile ();
137+
138+ resolved = true ;
185139
186140 break ;
187141 }
188142 }
143+
144+ if (!resolved ) {
145+ continue ;
146+ }
189147 }
190148
149+ // '../src/{Entity}'
150+ // '../src/*Controller.php'
191151 if (Arrays .stream (GLOB_DETECTION_CHARS ).anyMatch (resource ::contains )) {
192152 String path = directory .getPath ();
193153
@@ -196,62 +156,41 @@ public static Collection<Pair<VirtualFile, String>> getFileResources(@NotNull Pr
196156 String s1 = Paths .get (path + File .separatorChar + StringUtils .stripStart (resourceResolved , "\\ /" )).normalize ().toString ();
197157 String syntaxAndPattern = "glob:" + s1 ;
198158 if (FileSystems .getDefault ().getPathMatcher (syntaxAndPattern ).matches (Paths .get (virtualFile .getPath ()))) {
199- files .add (Pair .create (containingFile , resource ));
159+ if (consumer .apply (new Pair <>(containingFile , resource ))) {
160+ return ;
161+ }
200162 }
201163 } catch (PatternSyntaxException | InvalidPathException ignored ) {
202164 }
203165
204166 continue ;
205167 }
206168
169+ // '../src/FooController.php'
207170 VirtualFile relativeFile = VfsUtil .findRelativeFile (directory , resourceResolved .replace ("\\ " , "/" ).split ("/" ));
208- if (relativeFile != null ) {
209- String relativePath = VfsUtil .getRelativePath (virtualFile , relativeFile );
210- if (relativePath != null ) {
211- files .add (Pair .create (containingFile , resource ));
171+ if (relativeFile != null && relativeFile .equals (virtualFile )) {
172+ if (consumer .apply (new Pair <>(containingFile , resource ))) {
173+ return ;
212174 }
213175 }
214- }
215- }
216176
217- return files ;
218- }
219-
220- @ Nullable
221- public static String getBundleLocateName (@ NotNull Project project , @ NotNull VirtualFile virtualFile ) {
222- SymfonyBundle containingBundle = new SymfonyBundleUtil (project ).getContainingBundle (virtualFile );
223- if (containingBundle == null ) {
224- return null ;
225- }
226-
227- String relativePath = containingBundle .getRelativePath (virtualFile );
228- if (relativePath == null ) {
229- return null ;
230- }
231-
232- return "@" + containingBundle .getName () + "/" + relativePath ;
233- }
234-
235- /**
236- * Search for line definition of "@FooBundle/foo.xml"
237- */
238- @ NotNull
239- private static Collection <PsiElement > getBundleLocateStringDefinitions (@ NotNull Project project , final @ NotNull String bundleFileName ) {
240- final Collection <PsiElement > psiElements = new HashSet <>();
241- for (VirtualFile refVirtualFile : getFileResourceRefers (project , bundleFileName )) {
242- PsiFile psiFile = PsiManager .getInstance (project ).findFile (refVirtualFile );
243- if (psiFile == null ) {
244- continue ;
245- }
177+ // '..src/Controller'
178+ if (fileResourceContext .getContextType () == FileResourceContextTypeEnum .ROUTE ) {
179+ if (StringUtils .isNotBlank (resourceResolved )) {
180+ directory = VfsUtil .findRelativeFile (directory , resourceResolved .replace ("\\ " , "/" ).split ("/" ));
181+ if (directory == null ) {
182+ continue ;
183+ }
184+ }
246185
247- FileResourceVisitorUtil .visitFile (psiFile , consumer -> {
248- if (bundleFileName .equals (consumer .getResource ())) {
249- psiElements .add (consumer .getPsiElement ());
186+ if (VfsUtil .isAncestor (directory , virtualFile , false )) {
187+ if (consumer .apply (new Pair <>(containingFile , resource ))) {
188+ return ;
189+ }
190+ }
250191 }
251- });
192+ }
252193 }
253-
254- return psiElements ;
255194 }
256195
257196 @ Nullable
@@ -263,15 +202,6 @@ public static RelatedItemLineMarkerInfo<PsiElement> getFileImplementsLineMarker(
263202 return null ;
264203 }
265204
266- String bundleLocateName = FileResourceUtil .getBundleLocateName (project , virtualFile );
267- if (bundleLocateName != null && FileResourceUtil .getFileResourceRefers (project , bundleLocateName ).size () > 0 ) {
268- NavigationGutterIconBuilder <PsiElement > builder = NavigationGutterIconBuilder .create (PhpIcons .IMPLEMENTS )
269- .setTargets (NotNullLazyValue .lazy (new FileResourceBundleNotNullLazyValue (project , bundleLocateName )))
270- .setTooltipText ("Navigate to resource" );
271-
272- return builder .createLineMarkerInfo (psiFile );
273- }
274-
275205 if (hasFileResources (project , psiFile )) {
276206 NavigationGutterIconBuilder <PsiElement > builder = NavigationGutterIconBuilder .create (PhpIcons .IMPLEMENTS )
277207 .setTargets (NotNullLazyValue .lazy (new FileResourceNotNullLazyValue (project , virtualFile )))
@@ -288,69 +218,22 @@ public static RelatedItemLineMarkerInfo<PsiElement> getFileImplementsLineMarker(
288218 */
289219 @ Nullable
290220 public static RelatedItemLineMarkerInfo <PsiElement > getFileImplementsLineMarkerInFolderScope (@ NotNull PsiFile psiFile ) {
291- VirtualFile virtualFile = psiFile .getVirtualFile ();
292- if (virtualFile == null ) {
293- return null ;
294- }
295-
296- final Project project = psiFile .getProject ();
297- String bundleLocateName = FileResourceUtil .getBundleLocateName (project , virtualFile );
298- if (bundleLocateName == null ) {
221+ if (!hasFileResources (psiFile .getProject (), psiFile )) {
299222 return null ;
300223 }
301224
302- Set <String > names = new HashSet <>();
303- names .add (bundleLocateName );
304-
305- // strip filename
306- int i = bundleLocateName .lastIndexOf ("/" );
307- if (i > 0 ) {
308- names .add (bundleLocateName .substring (0 , i ));
309- }
310-
311- int targets = 0 ;
312- for (String name : names ) {
313- targets += FileResourceUtil .getFileResourceRefers (project , name ).size ();
314- }
315-
316- if (targets == 0 ) {
225+ VirtualFile virtualFile = psiFile .getVirtualFile ();
226+ if (virtualFile == null ) {
317227 return null ;
318228 }
319229
320230 NavigationGutterIconBuilder <PsiElement > builder = NavigationGutterIconBuilder .create (PlatformIcons .ANNOTATION_TYPE_ICON )
321- .setTargets (NotNullLazyValue .lazy (new FileResourceBundleNotNullLazyValue ( project , names )))
231+ .setTargets (NotNullLazyValue .lazy (new FileResourceNotNullLazyValue ( psiFile . getProject (), virtualFile )))
322232 .setTooltipText ("Symfony: <a href=\" https://symfony.com/doc/current/routing.html#creating-routes-as-annotations\" >Annotation Routing</a>" );
323233
324234 return builder .createLineMarkerInfo (psiFile );
325235 }
326236
327- private static class FileResourceBundleNotNullLazyValue implements Supplier <Collection <? extends PsiElement >> {
328-
329- private final Collection <String > resources ;
330- private final Project project ;
331-
332- public FileResourceBundleNotNullLazyValue (@ NotNull Project project , @ NotNull Collection <String > resource ) {
333- this .resources = resource ;
334- this .project = project ;
335- }
336-
337- public FileResourceBundleNotNullLazyValue (@ NotNull Project project , @ NotNull String resource ) {
338- this .resources = Collections .singleton (resource );
339- this .project = project ;
340- }
341-
342- @ Override
343- public Collection <? extends PsiElement > get () {
344- Collection <PsiElement > psiElements = new HashSet <>();
345-
346- for (String resource : this .resources ) {
347- psiElements .addAll (getBundleLocateStringDefinitions (project , resource ));
348- }
349-
350- return psiElements ;
351- }
352- }
353-
354237 /**
355238 * Gives targets to files on Bundle locate syntax. "@FooBundle/.../foo.yml"
356239 */
@@ -474,7 +357,7 @@ private static class FileResourceNotNullLazyValue implements Supplier<Collection
474357 private final Project project ;
475358 private final VirtualFile virtualFile ;
476359
477- public FileResourceNotNullLazyValue (Project project , VirtualFile virtualFile ) {
360+ public FileResourceNotNullLazyValue (@ NotNull Project project , @ NotNull VirtualFile virtualFile ) {
478361 this .project = project ;
479362 this .virtualFile = virtualFile ;
480363 }
0 commit comments