1
1
package fr .adrienbrault .idea .symfony2plugin .dic .container .util ;
2
2
3
3
import com .intellij .openapi .project .Project ;
4
+ import com .intellij .openapi .util .Key ;
5
+ import com .intellij .openapi .util .ModificationTracker ;
6
+ import com .intellij .openapi .vfs .VfsUtil ;
7
+ import com .intellij .openapi .vfs .VirtualFile ;
4
8
import com .intellij .patterns .PlatformPatterns ;
5
9
import com .intellij .psi .PsiElement ;
6
10
import com .intellij .psi .PsiFile ;
7
11
import com .intellij .psi .search .GlobalSearchScope ;
8
- import com .intellij .psi .util .PsiTreeUtil ;
12
+ import com .intellij .psi .util .* ;
9
13
import com .intellij .psi .xml .*;
10
14
import com .intellij .util .Consumer ;
11
15
import com .intellij .util .indexing .FileBasedIndex ;
37
41
import org .jetbrains .yaml .YAMLUtil ;
38
42
import org .jetbrains .yaml .psi .*;
39
43
44
+ import java .time .Instant ;
40
45
import java .util .*;
46
+ import java .util .stream .Collectors ;
47
+ import java .util .stream .Stream ;
41
48
42
49
/**
43
50
* @author Daniel Espendiller <daniel@espendiller.net>
@@ -55,6 +62,11 @@ public class ServiceContainerUtil {
55
62
new MethodMatcher .CallToSignature ("\\ Symfony\\ Bundle\\ FrameworkBundle\\ Controller\\ AbstractController" , "get" ),
56
63
};
57
64
65
+ private static ModificationTracker TIMED_MODIFICATION_TRACKER = new TimeSecondModificationTracker (60 );
66
+
67
+ private static final Key <CachedValue <Collection <String >>> SYMFONY_COMPILED_TIMED_SERVICE_WATCHER = new Key <>("SYMFONY_COMPILED_TIMED_SERVICE_WATCHER" );
68
+ private static final Key <CachedValue <Collection <String >>> SYMFONY_COMPILED_SERVICE_WATCHER = new Key <>("SYMFONY_COMPILED_SERVICE_WATCHER" );
69
+
58
70
private static String [] LOWER_PRIORITY = new String [] {
59
71
"debug" , "default" , "abstract" , "inner" , "chain" , "decorate" , "delegat"
60
72
};
@@ -627,4 +639,100 @@ public static List<String> getSortedServiceId(@NotNull Project project, @NotNull
627
639
628
640
return myIds ;
629
641
}
642
+
643
+ /**
644
+ * Provide a modification on nearest second value
645
+ */
646
+ private static class TimeSecondModificationTracker implements ModificationTracker {
647
+ private final int expiresAfter ;
648
+
649
+ public TimeSecondModificationTracker (int expiresAfter ) {
650
+ this .expiresAfter = expiresAfter ;
651
+ }
652
+
653
+ @ Override
654
+ public long getModificationCount () {
655
+ long unixTime = Instant .now ().getEpochSecond ();
656
+ return roundNearest (unixTime );
657
+ }
658
+
659
+ private long roundNearest (long n ) {
660
+ // Smaller multiple
661
+ long a = (n / this .expiresAfter ) * this .expiresAfter ;
662
+
663
+ // Larger multiple
664
+ long b = a + this .expiresAfter ;
665
+
666
+ // Return of closest of two
667
+ return (n - a > b - n ) ? b : a ;
668
+ }
669
+ }
670
+
671
+ /**
672
+ * Find compiled and cache it until any psi change occur
673
+ *
674
+ * - "app/cache/dev/appDevDebugProjectContainer.xml"
675
+ * - ...
676
+ */
677
+ public static Collection <String > getContainerFiles (@ NotNull Project project ) {
678
+ return CachedValuesManager .getManager (project )
679
+ .getCachedValue (
680
+ project ,
681
+ SYMFONY_COMPILED_SERVICE_WATCHER ,
682
+ () -> CachedValueProvider .Result .create (getContainerFilesInner (project ), PsiModificationTracker .MODIFICATION_COUNT ),
683
+ false
684
+ );
685
+ }
686
+
687
+ /**
688
+ * Find possible compiled service file with seconds cache
689
+ *
690
+ * - "app/cache/dev/appDevDebugProjectContainer.xml"
691
+ * - "var/cache/dev/appDevDebugProjectContainer.xml"
692
+ * - "var/cache/dev/srcDevDebugProjectContainer.xml"
693
+ * - "var/cache/dev/srcApp_KernelDevDebugContainer.xml"
694
+ * - "var/cache/dev/App_KernelDevDebugContainer.xml" // Symfony => 4 + flex
695
+ * - "app/cache/dev_392373729/appDevDebugProjectContainer.xml"
696
+ */
697
+ private static Collection <String > getContainerFilesInner (@ NotNull Project project ) {
698
+ return CachedValuesManager .getManager (project ).getCachedValue (project , SYMFONY_COMPILED_TIMED_SERVICE_WATCHER , () -> {
699
+ Set <String > files = new HashSet <>();
700
+
701
+ // several Symfony cache folder structures
702
+ for (String root : new String [] {"var/cache" , "app/cache" }) {
703
+ VirtualFile baseDir = project .getBaseDir ();
704
+
705
+ VirtualFile relativeFile = VfsUtil .findRelativeFile (root , baseDir );
706
+ if (relativeFile == null ) {
707
+ continue ;
708
+ }
709
+
710
+ // find a dev folder eg: "dev_392373729" or just "dev"
711
+ Set <VirtualFile > devFolders = Stream .of (relativeFile .getChildren ())
712
+ .filter (virtualFile -> virtualFile .isDirectory () && virtualFile .getName ().toLowerCase ().startsWith ("dev" ))
713
+ .collect (Collectors .toSet ());
714
+
715
+ for (VirtualFile devFolder : devFolders ) {
716
+ Set <String > debugContainers = Stream .of (devFolder .getChildren ())
717
+ .filter (virtualFile -> {
718
+ if (!"xml" .equalsIgnoreCase (virtualFile .getExtension ())) {
719
+ return false ;
720
+ }
721
+
722
+ // Some examples: App_KernelDevDebugContainer, appDevDebugProjectContainer
723
+ String filename = virtualFile .getName ().toLowerCase ();
724
+ return filename .contains ("debugcontainer" )
725
+ || (filename .contains ("debug" ) && filename .contains ("container" ))
726
+ || (filename .contains ("kernel" ) && filename .contains ("container" ));
727
+ })
728
+ .map (virtualFile -> VfsUtil .getRelativePath (virtualFile , baseDir , '/' ))
729
+ .collect (Collectors .toSet ());
730
+
731
+ files .addAll (debugContainers );
732
+ }
733
+ }
734
+
735
+ return CachedValueProvider .Result .create (files , TIMED_MODIFICATION_TRACKER );
736
+ }, false );
737
+ }
630
738
}
0 commit comments