Skip to content

Commit

Permalink
#2301 Support translation for "Symfony\Component\Translation\t"
Browse files Browse the repository at this point in the history
  • Loading branch information
Haehnchen committed Mar 21, 2024
1 parent 831618e commit 9469e79
Show file tree
Hide file tree
Showing 12 changed files with 253 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean is

return new PsiElementVisitor() {
@Override
public void visitElement(PsiElement element) {
public void visitElement(@NotNull PsiElement element) {
invoke(holder, element);
super.visitElement(element);
}
Expand All @@ -53,7 +53,7 @@ private void invoke(@NotNull ProblemsHolder holder, @NotNull PsiElement psiEleme
}

PsiElement methodReferenceOrNewExpression = parameterList.getContext();
if (!(methodReferenceOrNewExpression instanceof MethodReference) && !(methodReferenceOrNewExpression instanceof NewExpression)) {
if (!(methodReferenceOrNewExpression instanceof FunctionReference) && !(methodReferenceOrNewExpression instanceof NewExpression)) {
return;
}

Expand All @@ -63,10 +63,8 @@ private void invoke(@NotNull ProblemsHolder holder, @NotNull PsiElement psiEleme
ASTNode previousNonWhitespaceSibling1 = FormatterUtil.getPreviousNonWhitespaceSibling(previousNonWhitespaceSibling);
if (previousNonWhitespaceSibling1 != null && previousNonWhitespaceSibling1.getElementType() == PhpTokenTypes.IDENTIFIER) {
String text = previousNonWhitespaceSibling1.getText();
boolean isSupportedAttributeInsideContext = "domain".equals(text) && (
(methodReferenceOrNewExpression instanceof MethodReference && PhpElementsUtil.isMethodReferenceInstanceOf((MethodReference) methodReferenceOrNewExpression, TranslationUtil.PHP_TRANSLATION_SIGNATURES))
|| (methodReferenceOrNewExpression instanceof NewExpression && PhpElementsUtil.isNewExpressionPhpClassWithInstance((NewExpression) methodReferenceOrNewExpression, TranslationUtil.PHP_TRANSLATION_TRANSLATABLE_MESSAGE))
);
boolean isSupportedAttributeInsideContext = "domain".equals(text)
&& TranslationUtil.isTranslationReference((ParameterListOwner) methodReferenceOrNewExpression);

if (isSupportedAttributeInsideContext) {
annotateTranslationDomain((StringLiteralExpression) psiElement, holder);
Expand All @@ -87,15 +85,17 @@ private void invoke(@NotNull ProblemsHolder holder, @NotNull PsiElement psiEleme
}

public static int getDomainParameter(@NotNull PsiElement methodReferenceOrNewExpression) {
if (methodReferenceOrNewExpression instanceof MethodReference && PhpElementsUtil.isMethodReferenceInstanceOf((MethodReference) methodReferenceOrNewExpression, TranslationUtil.PHP_TRANSLATION_SIGNATURES)) {
if (methodReferenceOrNewExpression instanceof MethodReference methodReference && PhpElementsUtil.isMethodReferenceInstanceOf(methodReference, TranslationUtil.PHP_TRANSLATION_SIGNATURES)) {
int domainParameter = 2;

if("transChoice".equals(((MethodReference) methodReferenceOrNewExpression).getName())) {
if("transChoice".equals(methodReference.getName())) {
domainParameter = 3;
}

return domainParameter;
} else if(methodReferenceOrNewExpression instanceof NewExpression && PhpElementsUtil.isNewExpressionPhpClassWithInstance((NewExpression) methodReferenceOrNewExpression, TranslationUtil.PHP_TRANSLATION_TRANSLATABLE_MESSAGE)) {
} else if(methodReferenceOrNewExpression instanceof NewExpression newExpression && PhpElementsUtil.isNewExpressionPhpClassWithInstance(newExpression, TranslationUtil.PHP_TRANSLATION_TRANSLATABLE_MESSAGE)) {
return 2;
} else if(methodReferenceOrNewExpression instanceof FunctionReference functionReference && TranslationUtil.isFunctionReferenceTranslationTFunction(functionReference)) {
return 2;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.jetbrains.php.lang.psi.elements.MethodReference;
import com.jetbrains.php.lang.psi.elements.NewExpression;
import com.jetbrains.php.lang.psi.elements.ParameterList;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import com.jetbrains.php.lang.psi.elements.*;
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
import fr.adrienbrault.idea.symfony2plugin.translation.dict.TranslationUtil;
import fr.adrienbrault.idea.symfony2plugin.translation.inspection.TranslationKeyGuessTypoQuickFix;
Expand Down Expand Up @@ -51,18 +48,15 @@ private void invoke(@NotNull ProblemsHolder holder, @NotNull PsiElement psiEleme
}

PsiElement methodReferenceOrNewExpression = parameterList.getContext();
if (!(methodReferenceOrNewExpression instanceof MethodReference) && !(methodReferenceOrNewExpression instanceof NewExpression)) {
if (!(methodReferenceOrNewExpression instanceof NewExpression) && !(methodReferenceOrNewExpression instanceof FunctionReference)) {
return;
}

if (!PsiElementUtils.isCurrentParameter(psiElement, "id", 0)) {
return;
}

if (!(
(methodReferenceOrNewExpression instanceof MethodReference && PhpElementsUtil.isMethodReferenceInstanceOf((MethodReference) methodReferenceOrNewExpression, TranslationUtil.PHP_TRANSLATION_SIGNATURES)) ||
(methodReferenceOrNewExpression instanceof NewExpression && PhpElementsUtil.isNewExpressionPhpClassWithInstance((NewExpression) methodReferenceOrNewExpression, TranslationUtil.PHP_TRANSLATION_TRANSLATABLE_MESSAGE)))
) {
if (!TranslationUtil.isTranslationReference((ParameterListOwner) methodReferenceOrNewExpression)) {
return;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import com.jetbrains.php.lang.psi.elements.MethodReference;
import com.jetbrains.php.lang.psi.elements.NewExpression;
import com.jetbrains.php.lang.psi.elements.ParameterList;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import com.jetbrains.php.lang.psi.elements.*;
import fr.adrienbrault.idea.symfony2plugin.Symfony2ProjectComponent;
import fr.adrienbrault.idea.symfony2plugin.translation.dict.TranslationUtil;
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
Expand All @@ -27,12 +24,17 @@ public class TranslationNavigationCompletionContributor {
public static class Completion extends CompletionContributor {
public Completion() {
// $this->translator->trans('<caret>', [], '<caret>');
extend(CompletionType.BASIC, PlatformPatterns.or(PhpElementsUtil.getParameterInsideMethodReferencePattern(), PhpElementsUtil.getParameterInsideNewExpressionPattern()), new CompletionProvider<>() {
// new TranslatableMessage('<caret>', [], '<caret>');
// t('<caret>', [], '<caret>');
extend(CompletionType.BASIC, PlatformPatterns.or(
PhpElementsUtil.getParameterInsideMethodReferencePattern(),
PhpElementsUtil.getParameterInsideNewExpressionPattern(),
PhpElementsUtil.getParameterInsideFunctionReferencePattern()
), new CompletionProvider<>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) {
PsiElement psiElement = parameters.getOriginalPosition();

if (!(psiElement.getParent() instanceof StringLiteralExpression stringLiteralExpression)) {
if (psiElement == null || !(psiElement.getParent() instanceof StringLiteralExpression stringLiteralExpression)) {
return;
}

Expand All @@ -51,7 +53,11 @@ protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull
public static class GotoDeclaration implements GotoDeclarationHandler {
@Override
public PsiElement @Nullable [] getGotoDeclarationTargets(@Nullable PsiElement sourceElement, int offset, Editor editor) {
if (sourceElement != null && sourceElement.getParent() instanceof StringLiteralExpression stringLiteralExpression && (PhpElementsUtil.getParameterInsideMethodReferencePattern().accepts(sourceElement) || PhpElementsUtil.getParameterInsideNewExpressionPattern().accepts(sourceElement))) {
if (sourceElement != null && sourceElement.getParent() instanceof StringLiteralExpression stringLiteralExpression && (
PhpElementsUtil.getParameterInsideMethodReferencePattern().accepts(sourceElement)
|| PhpElementsUtil.getParameterInsideNewExpressionPattern().accepts(sourceElement)
|| PhpElementsUtil.getParameterInsideFunctionReferencePattern().accepts(sourceElement)
)) {
Collection<PsiElement> psiElements = new ArrayList<>();

Project project = sourceElement.getProject();
Expand All @@ -74,16 +80,15 @@ public static void visit(@NotNull StringLiteralExpression psiElement, @NotNull T
return;
}

PsiElement methodReferenceOrNewExpression = parameterList.getContext();
if (!(parameterList.getContext() instanceof ParameterListOwner parameterListOwner)) {
return;
};

if (!(
(methodReferenceOrNewExpression instanceof MethodReference && PhpElementsUtil.isMethodReferenceInstanceOf((MethodReference) methodReferenceOrNewExpression, TranslationUtil.PHP_TRANSLATION_SIGNATURES)) ||
(methodReferenceOrNewExpression instanceof NewExpression && PhpElementsUtil.isNewExpressionPhpClassWithInstance((NewExpression) methodReferenceOrNewExpression, TranslationUtil.PHP_TRANSLATION_TRANSLATABLE_MESSAGE)))
) {
if (!TranslationUtil.isTranslationReference(parameterListOwner)) {
return;
}

int domainParameter = PhpTranslationDomainInspection.getDomainParameter(methodReferenceOrNewExpression);
int domainParameter = PhpTranslationDomainInspection.getDomainParameter(parameterListOwner);

if (PsiElementUtils.isCurrentParameter(psiElement, "domain", domainParameter)) {
domain.accept(psiElement);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,7 @@
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression;
import com.jetbrains.php.lang.psi.elements.NewExpression;
import com.jetbrains.php.lang.psi.elements.ParameterList;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import com.jetbrains.php.lang.psi.elements.*;
import com.jetbrains.twig.elements.TwigElementTypes;
import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons;
import fr.adrienbrault.idea.symfony2plugin.templating.TwigPattern;
Expand Down Expand Up @@ -64,6 +61,12 @@ public void register(@NotNull GotoCompletionRegistrarParameter registrar) {
PlatformPatterns.psiElement().withParent(StringLiteralExpression.class),
new MyPhpTranslatableMessageCompletionContributor()
);

// t('symfony.great', ['test' => '%fo<caret>obar%'], 'symfony');
registrar.register(
PlatformPatterns.psiElement().withParent(StringLiteralExpression.class),
new MyPhpTranslatableMessageCompletionViaTFunctionContributor()
);
}

private static class MyTranslationPlaceholderGotoCompletionProvider extends GotoCompletionProvider {
Expand Down Expand Up @@ -146,6 +149,64 @@ public GotoCompletionProvider getProvider(@NotNull PsiElement psiElement) {
* new \Symfony\Component\Translation\TranslatableMessage('symfony.great', ['%fo<caret>obar%', null], 'symfony');
*/
private static class MyPhpTranslatableMessageCompletionContributor implements GotoCompletionContributor {
@Nullable
@Override
public GotoCompletionProvider getProvider(@NotNull PsiElement psiElement) {
PsiElement context = psiElement.getContext();
if (!(context instanceof StringLiteralExpression)) {
return null;
}

ArrayCreationExpression arrayCreationExpression = PhpElementsUtil.getCompletableArrayCreationElement(context);
if (arrayCreationExpression == null) {
return null;
}

PsiElement parameterList = arrayCreationExpression.getContext();
if (!(parameterList instanceof ParameterList)) {
return null;
}

PsiElement[] parameters = ((ParameterList) parameterList).getParameters();
int placeHolderParameter = 1;
if (parameters.length < placeHolderParameter) {
return null;
}

PsiElement newEx = parameterList.getContext();
if (!(newEx instanceof FunctionReference functionReference) || !TranslationUtil.isFunctionReferenceTranslationTFunction(functionReference)) {
return null;
}

ParameterBag currentIndex = PsiElementUtils.getCurrentParameterIndex(arrayCreationExpression);
if (currentIndex == null || currentIndex.getIndex() != placeHolderParameter) {
return null;
}

String key = PhpElementsUtil.getStringValue(parameters[0]);
if (key == null) {
return null;
}

String domain = "messages";
int domainParameter = 2;
if (parameters.length > domainParameter) {
domain = PhpElementsUtil.getStringValue(parameters[domainParameter]);
if(domain == null) {
return null;
}
}

return new MyTranslationPlaceholderGotoCompletionProvider(psiElement, key, domain);
}
}


/**
* t('symfony.great', ['test' => '%fo<caret>obar%'], 'symfony');
* t('symfony.great', ['%fo<caret>obar%', null], 'symfony');
*/
private static class MyPhpTranslatableMessageCompletionViaTFunctionContributor implements GotoCompletionContributor {
@Nullable
@Override
public GotoCompletionProvider getProvider(@NotNull PsiElement psiElement) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
import com.intellij.util.Consumer;
import com.intellij.util.indexing.FileBasedIndex;
import com.jetbrains.php.lang.psi.PhpFile;
import com.jetbrains.php.lang.psi.elements.Field;
import com.jetbrains.php.lang.psi.elements.PhpClass;
import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
import com.jetbrains.php.lang.psi.elements.*;
import fr.adrienbrault.idea.symfony2plugin.extension.TranslatorProvider;
import fr.adrienbrault.idea.symfony2plugin.extension.TranslatorProviderDict;
import fr.adrienbrault.idea.symfony2plugin.stubs.indexes.TranslationStubIndex;
Expand Down Expand Up @@ -410,6 +408,17 @@ public static Set<String> getPlaceholderFromTranslation(@NotNull String text) {
return placeholder;
}

public static boolean isFunctionReferenceTranslationTFunction(@NotNull FunctionReference functionReference) {
return "\\Symfony\\Component\\Translation\\t".equals(functionReference.getFQN());
}

public static boolean isTranslationReference(@NotNull ParameterListOwner psiElement) {
return (psiElement instanceof MethodReference methodReference && PhpElementsUtil.isMethodReferenceInstanceOf(methodReference, TranslationUtil.PHP_TRANSLATION_SIGNATURES))
|| (psiElement instanceof NewExpression newExpression && PhpElementsUtil.isNewExpressionPhpClassWithInstance(newExpression, TranslationUtil.PHP_TRANSLATION_TRANSLATABLE_MESSAGE))
|| (psiElement instanceof FunctionReference functionReference && TranslationUtil.isFunctionReferenceTranslationTFunction(functionReference));
}


@NotNull
private static TranslatorProvider[] getTranslationProviders() {
return Stream.concat(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,21 @@ static public PsiElementPattern.Capture<PsiElement> getParameterInsideNewExpress
.withLanguage(PhpLanguage.INSTANCE);
}

static public PsiElementPattern.Capture<PsiElement> getParameterInsideFunctionReferencePattern() {
return PlatformPatterns
.psiElement()
.withParent(
PlatformPatterns.psiElement(StringLiteralExpression.class)
.withParent(
PlatformPatterns.psiElement(ParameterList.class)
.withParent(
PlatformPatterns.psiElement(FunctionReference.class)
)
)
)
.withLanguage(PhpLanguage.INSTANCE);
}

/**
* class "Foo" extends
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,30 @@ public void testThatPhpTranslationDomainInspectionsForTranslatableMessageAreProv
PhpTranslationDomainInspection.MESSAGE
);
}

public void testThatPhpTranslationDomainInspectionsForTranslatableMessageViaTFunctionAreProvided() {
assertLocalInspectionContains("test.php", "<?php\n" +
"use function Symfony\\Component\\Translation\\t;\n" +
"t('foobar', [], 'do<caret>main');",
PhpTranslationDomainInspection.MESSAGE
);

assertLocalInspectionContains("test.php", "<?php\n" +
"use function Symfony\\Component\\Translation\\t;\n" +
"t('foobar', [], 'foo<caret>bar');",
PhpTranslationDomainInspection.MESSAGE
);

assertLocalInspectionNotContains("test.php", "<?php\n" +
"use function Symfony\\Component\\Translation\\t;\n" +
"t('foobar', [], 'sym<caret>fony');",
PhpTranslationDomainInspection.MESSAGE
);

assertLocalInspectionNotContains("test.php", "<?php\n" +
"use function Symfony\\Component\\Translation\\t;\n" +
"t(domain: 'sym<caret>fony');",
PhpTranslationDomainInspection.MESSAGE
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,30 @@ public void testThatPhpTranslationKeyInspectionsForTranslatableMessageAreProvide
PhpTranslationKeyInspection.MESSAGE
);
}

public void testThatPhpTranslationKeyInspectionsForTranslatableMessageViaTFunctionAreProvided() {
assertLocalInspectionContains("test.php", "<?php\n" +
"use function Symfony\\Component\\Translation\\t;\n" +
"t('symfon<caret>y.great');",
PhpTranslationKeyInspection.MESSAGE
);

assertLocalInspectionNotContains("test.php", "<?php\n" +
"use function Symfony\\Component\\Translation\\t;\n" +
"t('symfon<caret>y.great', domain: 'symfony');",
PhpTranslationKeyInspection.MESSAGE
);

assertLocalInspectionNotContains("test.php", "<?php\n" +
"use function Symfony\\Component\\Translation\\t;\n" +
"t('symfon<caret>y.great', [], 'symfony');",
PhpTranslationKeyInspection.MESSAGE
);

assertLocalInspectionNotContains("test.php", "<?php\n" +
"use function Symfony\\Component\\Translation\\t;\n" +
"t('symfon<caret>y.great', 1, [], $x);",
PhpTranslationKeyInspection.MESSAGE
);
}
}
Loading

0 comments on commit 9469e79

Please sign in to comment.