Skip to content

Commit d008eea

Browse files
committed
provide better detection for translation directory inside based on the cached "translations" folder
1 parent 9e4c9da commit d008eea

File tree

4 files changed

+131
-130
lines changed

4 files changed

+131
-130
lines changed

src/main/java/fr/adrienbrault/idea/symfony2plugin/dic/container/util/ServiceContainerUtil.java

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher;
3333
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
3434
import fr.adrienbrault.idea.symfony2plugin.util.PsiElementUtils;
35+
import fr.adrienbrault.idea.symfony2plugin.util.TimeSecondModificationTracker;
3536
import fr.adrienbrault.idea.symfony2plugin.util.dict.ServiceUtil;
3637
import fr.adrienbrault.idea.symfony2plugin.util.psi.PsiElementAssertUtil;
3738
import fr.adrienbrault.idea.symfony2plugin.util.yaml.YamlHelper;
@@ -41,7 +42,6 @@
4142
import org.jetbrains.yaml.YAMLUtil;
4243
import org.jetbrains.yaml.psi.*;
4344

44-
import java.time.Instant;
4545
import java.util.*;
4646
import java.util.stream.Collectors;
4747
import java.util.stream.Stream;
@@ -62,8 +62,6 @@ public class ServiceContainerUtil {
6262
new MethodMatcher.CallToSignature("\\Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController", "get"),
6363
};
6464

65-
private static ModificationTracker TIMED_MODIFICATION_TRACKER = new TimeSecondModificationTracker(60);
66-
6765
private static final Key<CachedValue<Collection<String>>> SYMFONY_COMPILED_TIMED_SERVICE_WATCHER = new Key<>("SYMFONY_COMPILED_TIMED_SERVICE_WATCHER");
6866
private static final Key<CachedValue<Collection<String>>> SYMFONY_COMPILED_SERVICE_WATCHER = new Key<>("SYMFONY_COMPILED_SERVICE_WATCHER");
6967

@@ -640,34 +638,6 @@ public static List<String> getSortedServiceId(@NotNull Project project, @NotNull
640638
return myIds;
641639
}
642640

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-
671641
/**
672642
* Find compiled and cache it until any psi change occur
673643
*
@@ -732,7 +702,7 @@ private static Collection<String> getContainerFilesInner(@NotNull Project projec
732702
}
733703
}
734704

735-
return CachedValueProvider.Result.create(files, TIMED_MODIFICATION_TRACKER);
705+
return CachedValueProvider.Result.create(files, TimeSecondModificationTracker.TIMED_MODIFICATION_TRACKER_60);
736706
}, false);
737707
}
738708
}
Lines changed: 73 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,45 @@
11
package fr.adrienbrault.idea.symfony2plugin.translation;
22

33
import com.intellij.openapi.project.Project;
4+
import com.intellij.openapi.util.Key;
45
import com.intellij.openapi.util.io.FileUtil;
6+
import com.intellij.openapi.vfs.VfsUtil;
7+
import com.intellij.openapi.vfs.VfsUtilCore;
8+
import com.intellij.openapi.vfs.VirtualFile;
9+
import com.intellij.psi.util.CachedValue;
10+
import com.intellij.psi.util.CachedValueProvider;
11+
import com.intellij.psi.util.CachedValuesManager;
12+
import com.intellij.psi.util.PsiModificationTracker;
513
import fr.adrienbrault.idea.symfony2plugin.Settings;
614
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
15+
import fr.adrienbrault.idea.symfony2plugin.dic.container.util.ServiceContainerUtil;
716
import fr.adrienbrault.idea.symfony2plugin.translation.parser.TranslationPsiParser;
817
import fr.adrienbrault.idea.symfony2plugin.translation.parser.TranslationStringMap;
18+
import fr.adrienbrault.idea.symfony2plugin.util.TimeSecondModificationTracker;
19+
import org.apache.commons.lang.StringUtils;
20+
import org.jetbrains.annotations.NotNull;
921
import org.jetbrains.annotations.Nullable;
1022

1123
import java.io.File;
12-
import java.util.HashMap;
13-
import java.util.Map;
24+
import java.util.*;
25+
import java.util.stream.Collectors;
1426

1527
/**
1628
* @author Daniel Espendiller <daniel@espendiller.net>
1729
*/
1830
public class TranslationIndex {
31+
private static final Key<CachedValue<Collection<File>>> SYMFONY_TRANSLATION_COMPILED_TIMED_WATCHER = new Key<>("SYMFONY_TRANSLATION_COMPILED_TIMED_WATCHER");
32+
private static final Key<CachedValue<Collection<File>>> SYMFONY_TRANSLATION_COMPILED = new Key<>("SYMFONY_TRANSLATION_COMPILED");
1933

20-
protected static Map<Project, TranslationIndex> instance = new HashMap<>();
34+
private static Map<Project, TranslationIndex> instance = new HashMap<>();
2135

22-
protected Project project;
36+
private Project project;
2337

2438
@Nullable
2539
private TranslationStringMap translationStringMap;
26-
private Long translationStringMapModified;
27-
28-
public static TranslationIndex getInstance(Project project){
40+
private long translationStringMapModified;
2941

42+
public static TranslationIndex getInstance(@NotNull Project project){
3043
TranslationIndex projectInstance = instance.get(project);
3144
if(projectInstance != null) {
3245
return projectInstance;
@@ -36,102 +49,92 @@ public static TranslationIndex getInstance(Project project){
3649
instance.put(project, projectInstance);
3750

3851
return projectInstance;
39-
4052
}
4153

42-
public TranslationIndex(Project project) {
54+
private TranslationIndex(@NotNull Project project) {
4355
this.project = project;
4456
}
4557

46-
4758
synchronized public TranslationStringMap getTranslationMap() {
48-
4959
if(this.translationStringMap != null && this.isCacheValid()) {
5060
return this.translationStringMap;
5161
}
5262

53-
File translationDirectory = this.getTranslationRoot();
54-
if(null == translationDirectory) {
63+
Collection<File> translationDirectories = this.getTranslationRoot();
64+
if(translationDirectories.size() == 0) {
5565
return new TranslationStringMap();
5666
}
5767

58-
Symfony2ProjectComponent.getLogger().info("translations changed: " + translationDirectory.toString());
68+
Symfony2ProjectComponent.getLogger().info("translations changed: " + StringUtils.join(translationDirectories.stream().map(File::toString).collect(Collectors.toSet()), ","));
5969

60-
this.translationStringMapModified = translationDirectory.lastModified();
61-
return this.translationStringMap = new TranslationPsiParser(project).parsePathMatcher(translationDirectory.getPath());
70+
this.translationStringMapModified = translationDirectories.stream().mapToLong(File::lastModified).sum();
71+
return this.translationStringMap = new TranslationPsiParser(project, translationDirectories).parsePathMatcher();
6272
}
6373

64-
protected boolean isCacheValid() {
65-
74+
private boolean isCacheValid() {
6675
// symfony2 recreates translation file on change, so folder modtime is caching indicator
67-
File translationRootPath = this.getTranslationRoot();
68-
if (null == translationRootPath) {
76+
Collection<File> translationDirectories = this.getTranslationRoot();
77+
if(translationDirectories.size() == 0) {
6978
return false;
7079
}
7180

72-
Long translationModified = translationRootPath.lastModified();
73-
if(!translationModified.equals(translationStringMapModified)) {
74-
return false;
75-
}
76-
77-
// @TODO make this more abstract
78-
// we check for possible file modifications here per translation file
79-
if(this.translationStringMap != null) {
81+
return translationDirectories.stream().mapToLong(File::lastModified).sum() == translationStringMapModified;
82+
}
8083

84+
@NotNull
85+
private Collection<File> getTranslationRoot() {
86+
return CachedValuesManager.getManager(project)
87+
.getCachedValue(
88+
project,
89+
SYMFONY_TRANSLATION_COMPILED,
90+
() -> CachedValueProvider.Result.create(getTranslationRootInnerTime(), PsiModificationTracker.MODIFICATION_COUNT),
91+
false
92+
);
93+
}
8194

82-
File file = new File(translationRootPath.getPath());
95+
@NotNull
96+
private Collection<File> getTranslationRootInnerTime() {
97+
return CachedValuesManager.getManager(project).getCachedValue(project, SYMFONY_TRANSLATION_COMPILED_TIMED_WATCHER, () -> CachedValueProvider.Result.create(getTranslationRootInner(), TimeSecondModificationTracker.TIMED_MODIFICATION_TRACKER_60), false);
98+
}
8399

84-
// use cache in any i/o error
85-
File[] files = file.listFiles();
86-
if(null == files) {
87-
return true;
88-
}
100+
@NotNull
101+
private Collection<File> getTranslationRootInner() {
102+
Collection<File> files = new HashSet<>();
89103

90-
// directory is empty or not exits, before and after instance
91-
Map<String, Long> fileNames = this.translationStringMap.getFileNames();
92-
if(files.length == 0 && fileNames.size() == 0) {
93-
return true;
104+
String translationPath = Settings.getInstance(project).pathToTranslation;
105+
if (StringUtils.isNotBlank(translationPath)) {
106+
if (!FileUtil.isAbsolute(translationPath)) {
107+
translationPath = project.getBasePath() + "/" + translationPath;
94108
}
95109

96-
for (File fileEntry : files) {
97-
if (!fileEntry.isDirectory()) {
98-
String fileName = fileEntry.getName();
99-
if(fileName.startsWith("catalogue") && fileName.endsWith("php")) {
100-
101-
102-
if(!fileNames.containsKey(fileName)) {
103-
return false;
104-
}
105-
106-
if(!fileNames.get(fileName).equals(fileEntry.lastModified())) {
107-
return false;
108-
}
109-
110-
}
111-
}
110+
File file = new File(translationPath);
111+
if(file.exists() && file.isDirectory()) {
112+
files.add(file);
112113
}
113-
114-
115114
}
116115

117-
return true;
118-
}
116+
VirtualFile baseDir = project.getBaseDir();
119117

120-
@Nullable
121-
protected File getTranslationRoot() {
118+
for (String containerFile : ServiceContainerUtil.getContainerFiles(project)) {
119+
// resolve the file
120+
VirtualFile containerVirtualFile = VfsUtil.findRelativeFile(containerFile, baseDir);
121+
if (containerVirtualFile == null) {
122+
continue;
123+
}
122124

123-
String translationPath = Settings.getInstance(this.project).pathToTranslation;
124-
if (!FileUtil.isAbsolute(translationPath)) {
125-
translationPath = project.getBasePath() + "/" + translationPath;
126-
}
125+
// get directory of the file; translation folder is same directory
126+
VirtualFile cacheDirectory = containerVirtualFile.getParent();
127+
if (cacheDirectory == null) {
128+
continue;
129+
}
127130

128-
File file = new File(translationPath);
129-
if(!file.exists() || !file.isDirectory()) {
130-
return null;
131+
// get translation sub directory
132+
VirtualFile translations = cacheDirectory.findChild("translations");
133+
if (translations != null) {
134+
files.add(VfsUtilCore.virtualToIoFile(translations));
135+
}
131136
}
132137

133-
return file;
138+
return files;
134139
}
135-
136-
137140
}

src/main/java/fr/adrienbrault/idea/symfony2plugin/translation/parser/TranslationPsiParser.java

Lines changed: 18 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.jetbrains.php.lang.psi.elements.*;
1212
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
1313
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
14+
import org.jetbrains.annotations.NotNull;
1415

1516
import java.io.File;
1617
import java.io.IOException;
@@ -20,39 +21,38 @@
2021
* @author Daniel Espendiller <daniel@espendiller.net>
2122
*/
2223
public class TranslationPsiParser {
24+
@NotNull
25+
private final Project project;
2326

24-
private Project project;
27+
@NotNull
28+
private final Collection<File> paths;
29+
30+
@NotNull final
2531
private TranslationStringMap translationStringMap;
2632

27-
public TranslationPsiParser(Project project) {
33+
public TranslationPsiParser(@NotNull Project project, @NotNull Collection<File> paths) {
2834
this.project = project;
35+
this.paths = paths;
2936
this.translationStringMap = new TranslationStringMap();
3037
}
3138

32-
public TranslationStringMap parsePathMatcher(String path) {
33-
34-
File file = new File(path);
35-
File[] files = file.listFiles();
36-
37-
if(null == files) {
38-
return this.translationStringMap;
39-
}
39+
public TranslationStringMap parsePathMatcher() {
40+
for (File path : paths) {
41+
File[] files = path.listFiles((directory, s) -> s.startsWith("catalogue") && s.endsWith("php"));
42+
if(null == files || files.length == 0) {
43+
continue;
44+
}
4045

41-
for (final File fileEntry : files) {
42-
if (!fileEntry.isDirectory()) {
43-
String fileName = fileEntry.getName();
44-
if(fileName.startsWith("catalogue") && fileName.endsWith("php")) {
45-
this.parse(fileEntry);
46-
this.translationStringMap.addFile(fileName, fileEntry.lastModified());
47-
}
46+
for (final File fileEntry : files) {
47+
this.parse(fileEntry);
48+
this.translationStringMap.addFile(fileEntry.getName(), fileEntry.lastModified());
4849
}
4950
}
5051

5152
return this.translationStringMap;
5253
}
5354

5455
public void parse(File file) {
55-
5656
VirtualFile virtualFile = VfsUtil.findFileByIoFile(file, true);
5757
if(virtualFile == null) {
5858
Symfony2ProjectComponent.getLogger().info("VfsUtil missing translation: " + file.getPath());
@@ -82,17 +82,12 @@ public void parse(File file) {
8282
if(phpClass != null && PhpElementsUtil.isInstanceOf(phpClass, "\\Symfony\\Component\\Translation\\MessageCatalogueInterface")) {
8383
this.getTranslationMessages(newExpression);
8484
}
85-
8685
}
87-
8886
}
89-
9087
}
91-
9288
}
9389

9490
private void getTranslationMessages(NewExpression newExpression) {
95-
9691
// first parameter hold our huge translation arrays
9792
PsiElement[] parameters = newExpression.getParameters();
9893
if(parameters.length < 2 || !(parameters[1] instanceof ArrayCreationExpression)) {
@@ -111,11 +106,8 @@ private void getTranslationMessages(NewExpression newExpression) {
111106
if(arrayValue instanceof ArrayCreationExpression) {
112107
getTransKeys(transDomain, (ArrayCreationExpression) arrayValue);
113108
}
114-
115109
}
116-
117110
}
118-
119111
}
120112

121113
private void getTransKeys(String domain, ArrayCreationExpression translationArray) {
@@ -127,7 +119,5 @@ private void getTransKeys(String domain, ArrayCreationExpression translationArra
127119
this.translationStringMap.addString(domain, transKey);
128120
}
129121
}
130-
131122
}
132-
133123
}

0 commit comments

Comments
 (0)