Skip to content

Commit 76b5dcf

Browse files
committed
use intention for Twig key creation, drops redundant warnings #443
1 parent 50e1b3e commit 76b5dcf

File tree

6 files changed

+160
-64
lines changed

6 files changed

+160
-64
lines changed

META-INF/plugin.xml

+6
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,12 @@
562562
<descriptionDirectoryName>DoctrineRepositoryClassConstantIntention</descriptionDirectoryName>
563563
</intentionAction>
564564

565+
<intentionAction>
566+
<className>fr.adrienbrault.idea.symfony2plugin.translation.intention.TwigTranslationKeyIntention</className>
567+
<category>Twig</category>
568+
<descriptionDirectoryName>TwigTranslationKeyIntention</descriptionDirectoryName>
569+
</intentionAction>
570+
565571
<!-- not ready to throw it out for all
566572
<toolWindow id="Web-Profiler" anchor="bottom" secondary="false"
567573
icon="/src/fr/adrienbrault/idea/symfony2plugin/icons/symfony_tool_window.png"

src/fr/adrienbrault/idea/symfony2plugin/templating/TwigAnnotator.java

-61
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,20 @@
22

33
import com.intellij.lang.annotation.AnnotationHolder;
44
import com.intellij.lang.annotation.Annotator;
5-
import com.intellij.openapi.project.Project;
65
import com.intellij.psi.PsiElement;
7-
import com.intellij.psi.PsiFile;
8-
import com.intellij.psi.search.GlobalSearchScope;
9-
import com.intellij.util.indexing.FileBasedIndex;
106
import fr.adrienbrault.idea.symfony2plugin.Settings;
117
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
128
import fr.adrienbrault.idea.symfony2plugin.TwigHelper;
139
import fr.adrienbrault.idea.symfony2plugin.asset.dic.AssetDirectoryReader;
1410
import fr.adrienbrault.idea.symfony2plugin.asset.dic.AssetFile;
1511
import fr.adrienbrault.idea.symfony2plugin.routing.PhpRoutingAnnotator;
16-
import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.YamlTranslationStubIndex;
1712
import fr.adrienbrault.idea.symfony2plugin.templating.assets.TwigNamedAssetsServiceParser;
1813
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
19-
import fr.adrienbrault.idea.symfony2plugin.translation.TranslationKeyIntentionAndQuickFixAction;
20-
import fr.adrienbrault.idea.symfony2plugin.translation.dict.TranslationUtil;
2114
import fr.adrienbrault.idea.symfony2plugin.util.service.ServiceXmlParserFactory;
2215
import org.apache.commons.lang.StringUtils;
2316
import org.jetbrains.annotations.NotNull;
2417

25-
import java.util.Collection;
26-
import java.util.List;
2718
import java.util.Set;
28-
import java.util.stream.Collectors;
2919

3020
/**
3121
* @author Daniel Espendiller <daniel@espendiller.net>
@@ -54,30 +44,6 @@ public void annotate(@NotNull final PsiElement element, @NotNull AnnotationHolde
5444
if(Settings.getInstance(element.getProject()).twigAnnotateTemplate) {
5545
this.annotateTemplate(element, holder);
5646
}
57-
58-
this.annotateTranslationKey(element, holder);
59-
}
60-
61-
private void annotateTranslationKey(@NotNull PsiElement psiElement, @NotNull AnnotationHolder holder) {
62-
if(!TwigHelper.getTranslationPattern("trans", "transchoice").accepts(psiElement)) {
63-
return;
64-
}
65-
66-
String text = psiElement.getText();
67-
if(StringUtils.isBlank(text)) {
68-
return;
69-
}
70-
71-
// get domain on file scope or method parameter
72-
String domainName = TwigUtil.getPsiElementTranslationDomain(psiElement);
73-
74-
// inspection will take care of complete unknown key
75-
if(!TranslationUtil.hasTranslationKey(psiElement.getProject(), text, domainName)) {
76-
return;
77-
}
78-
79-
holder.createInfoAnnotation(psiElement, "Create translation key")
80-
.registerFix(new TranslationKeyIntentionAndQuickFixAction(text, domainName, new MyKeyDomainNotExistingCollector()));
8147
}
8248

8349
private void annotateRoute(@NotNull final PsiElement element, @NotNull AnnotationHolder holder) {
@@ -166,31 +132,4 @@ private boolean isKnownAssetFileOrFolder(PsiElement element, String templateName
166132

167133
return TwigHelper.resolveAssetsFiles(element.getProject(), templateName, fileTypes).size() > 0;
168134
}
169-
170-
/**
171-
* Collect all domain files that are not providing the given key
172-
* Known VirtualFiles are filtered out based on the index
173-
*/
174-
private static class MyKeyDomainNotExistingCollector implements TranslationKeyIntentionAndQuickFixAction.DomainCollector {
175-
@NotNull
176-
@Override
177-
public Collection<PsiFile> collect(@NotNull Project project, @NotNull String key, @NotNull String domain) {
178-
return TranslationUtil.getDomainPsiFiles(project, domain).stream()
179-
.filter(psiFile -> !isDomainAndKeyInPsi(psiFile, key, domain))
180-
.collect(Collectors.toList());
181-
}
182-
183-
private boolean isDomainAndKeyInPsi(@NotNull PsiFile psiFile, @NotNull String key, @NotNull String domain) {
184-
List<Set<String>> values = FileBasedIndex.getInstance()
185-
.getValues(YamlTranslationStubIndex.KEY, domain, GlobalSearchScope.fileScope(psiFile));
186-
187-
for (Set<String> value : values) {
188-
if(value.contains(key)) {
189-
return true;
190-
}
191-
}
192-
193-
return false;
194-
}
195-
}
196135
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package fr.adrienbrault.idea.symfony2plugin.translation.intention;
2+
3+
import com.intellij.codeInsight.intention.PsiElementBaseIntentionAction;
4+
import com.intellij.openapi.editor.Editor;
5+
import com.intellij.openapi.project.Project;
6+
import com.intellij.openapi.util.Pair;
7+
import com.intellij.psi.PsiElement;
8+
import com.intellij.psi.PsiFile;
9+
import com.intellij.psi.search.GlobalSearchScope;
10+
import com.intellij.util.IncorrectOperationException;
11+
import com.intellij.util.indexing.FileBasedIndex;
12+
import fr.adrienbrault.idea.symfony2plugin.TwigHelper;
13+
import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.YamlTranslationStubIndex;
14+
import fr.adrienbrault.idea.symfony2plugin.templating.util.TwigUtil;
15+
import fr.adrienbrault.idea.symfony2plugin.translation.TranslationKeyIntentionAndQuickFixAction;
16+
import fr.adrienbrault.idea.symfony2plugin.translation.dict.TranslationUtil;
17+
import org.apache.commons.lang.StringUtils;
18+
import org.jetbrains.annotations.Nls;
19+
import org.jetbrains.annotations.NotNull;
20+
import org.jetbrains.annotations.Nullable;
21+
22+
import java.util.Collection;
23+
import java.util.List;
24+
import java.util.Set;
25+
import java.util.stream.Collectors;
26+
27+
/**
28+
* @author Daniel Espendiller <daniel@espendiller.net>
29+
*/
30+
public class TwigTranslationKeyIntention extends PsiElementBaseIntentionAction {
31+
@Override
32+
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement psiElement) throws IncorrectOperationException {
33+
Pair<String, String> pair = getKeyAndDomain(psiElement);
34+
if(pair == null) {
35+
return;
36+
}
37+
38+
new TranslationKeyIntentionAndQuickFixAction(pair.getFirst(), pair.getSecond(), new MyKeyDomainNotExistingCollector())
39+
.invoke(project, editor, psiElement.getContainingFile());
40+
}
41+
42+
@Override
43+
public boolean isAvailable(@NotNull Project project, Editor editor, @NotNull PsiElement psiElement) {
44+
return getKeyAndDomain(psiElement) != null;
45+
}
46+
47+
@Nls
48+
@NotNull
49+
@Override
50+
public String getFamilyName() {
51+
return "Symfony";
52+
}
53+
54+
@NotNull
55+
@Override
56+
public String getText() {
57+
return "Symfony: create translation key";
58+
}
59+
60+
@Nullable
61+
private Pair<String, String> getKeyAndDomain(@NotNull PsiElement psiElement) {
62+
if(!TwigHelper.getTranslationPattern("trans", "transchoice").accepts(psiElement)) {
63+
return null;
64+
}
65+
66+
String key = psiElement.getText();
67+
if(StringUtils.isBlank(key)) {
68+
return null;
69+
}
70+
71+
// get domain on file scope or method parameter
72+
String domainName = TwigUtil.getPsiElementTranslationDomain(psiElement);
73+
74+
// inspection will take care of complete unknown key
75+
if(!TranslationUtil.hasTranslationKey(psiElement.getProject(), key, domainName)) {
76+
return null;
77+
}
78+
79+
return Pair.create(key, domainName);
80+
}
81+
82+
/**
83+
* Collect all domain files that are not providing the given key
84+
* Known VirtualFiles are filtered out based on the index
85+
*/
86+
private static class MyKeyDomainNotExistingCollector implements TranslationKeyIntentionAndQuickFixAction.DomainCollector {
87+
@NotNull
88+
@Override
89+
public Collection<PsiFile> collect(@NotNull Project project, @NotNull String key, @NotNull String domain) {
90+
return TranslationUtil.getDomainPsiFiles(project, domain).stream()
91+
.filter(psiFile -> !isDomainAndKeyInPsi(psiFile, key, domain))
92+
.collect(Collectors.toList());
93+
}
94+
95+
private boolean isDomainAndKeyInPsi(@NotNull PsiFile psiFile, @NotNull String key, @NotNull String domain) {
96+
List<Set<String>> values = FileBasedIndex.getInstance()
97+
.getValues(YamlTranslationStubIndex.KEY, domain, GlobalSearchScope.fileScope(psiFile));
98+
99+
for (Set<String> value : values) {
100+
if(value.contains(key)) {
101+
return true;
102+
}
103+
}
104+
105+
return false;
106+
}
107+
}
108+
}

tests/fr/adrienbrault/idea/symfony2plugin/tests/SymfonyLightCodeInsightFixtureTestCase.java

+14-3
Original file line numberDiff line numberDiff line change
@@ -487,13 +487,24 @@ public void assertIntentionIsAvailable(LanguageFileType languageFileType, String
487487
myFixture.configureByText(languageFileType, configureByText);
488488
PsiElement psiElement = myFixture.getFile().findElementAt(myFixture.getCaretOffset());
489489

490+
Set<String> items = new HashSet<>();
491+
490492
for (IntentionAction intentionAction : IntentionManager.getInstance().getIntentionActions()) {
491-
if(intentionAction.isAvailable(getProject(), getEditor(), psiElement.getContainingFile()) && intentionAction.getText().equals(intentionText)) {
492-
return;
493+
if(!intentionAction.isAvailable(getProject(), getEditor(), psiElement.getContainingFile())) {
494+
continue;
493495
}
496+
497+
String text = intentionAction.getText();
498+
items.add(text);
499+
500+
if(!text.equals(intentionText)) {
501+
continue;
502+
}
503+
504+
return;
494505
}
495506

496-
fail(String.format("Fail intention action '%s' is available in element '%s'", intentionText, psiElement.getText()));
507+
fail(String.format("Fail intention action '%s' is available in element '%s' with '%s'", intentionText, psiElement.getText(), items));
497508
}
498509

499510
public void assertLocalInspectionNotContains(String filename, String content, String contains) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package fr.adrienbrault.idea.symfony2plugin.tests.translation.intention;
2+
3+
import com.jetbrains.twig.TwigFileType;
4+
import fr.adrienbrault.idea.symfony2plugin.tests.SymfonyLightCodeInsightFixtureTestCase;
5+
6+
import java.io.File;
7+
8+
/**
9+
* @author Daniel Espendiller <daniel@espendiller.net>
10+
* @see fr.adrienbrault.idea.symfony2plugin.translation.intention.TwigTranslationKeyIntention
11+
*/
12+
public class TwigTranslationKeyIntentionTest extends SymfonyLightCodeInsightFixtureTestCase {
13+
public void setUp() throws Exception {
14+
super.setUp();
15+
16+
myFixture.copyFileToProject("symfony.de.yml", "Resources/translations/symfony.de.yml");
17+
}
18+
19+
public String getTestDataPath() {
20+
return new File(this.getClass().getResource("fixtures").getFile()).getAbsolutePath();
21+
}
22+
23+
public void testThatKeyAlreadyExistsAndProvidesIntentionForOtherDomains() {
24+
assertIntentionIsAvailable(
25+
TwigFileType.INSTANCE,
26+
"{{ 'symfo<caret>ny.great'|trans({}, 'symfony')) }}",
27+
"Symfony: create translation key"
28+
);
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
symfony:
2+
great: 'YAML Symfony2 is great'

0 commit comments

Comments
 (0)