From 5dc2eefe892eeb8e8ea3bd1ab2c1cc85d40ed42f Mon Sep 17 00:00:00 2001 From: adelf Date: Thu, 3 Jun 2021 00:18:10 +0300 Subject: [PATCH] java support GitOrigin-RevId: 1100eb4fd41061ddcaefa6d57d024f819c9f479b --- plugins/env-files-support/build.gradle | 1 + .../dotenv/java/JavaCompletionConfidence.java | 25 ++++ .../java/JavaEnvCompletionContributor.java | 76 ++++++++++ .../java/JavaEnvironmentCallsVisitor.java | 39 +++++ ...avaEnvironmentVariablesUsagesProvider.java | 32 ++++ .../adelf/idea/dotenv/java/JavaPsiHelper.java | 137 ++++++++++++++++++ .../src/main/resources/META-INF/java.xml | 12 ++ .../src/main/resources/META-INF/plugin.xml | 1 + .../dotenv/tests/usages/JavaUsagesTest.java | 13 ++ .../tests/usages/fixtures/java/System.java | 11 ++ .../tests/usages/fixtures/java/Usages.java | 7 + 11 files changed, 354 insertions(+) create mode 100644 plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaCompletionConfidence.java create mode 100644 plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaEnvCompletionContributor.java create mode 100644 plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaEnvironmentCallsVisitor.java create mode 100644 plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaEnvironmentVariablesUsagesProvider.java create mode 100644 plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaPsiHelper.java create mode 100644 plugins/env-files-support/src/main/resources/META-INF/java.xml create mode 100644 plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/usages/JavaUsagesTest.java create mode 100644 plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/usages/fixtures/java/System.java create mode 100644 plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/usages/fixtures/java/Usages.java diff --git a/plugins/env-files-support/build.gradle b/plugins/env-files-support/build.gradle index 9f51f1004fe8..4bc7321f6c70 100644 --- a/plugins/env-files-support/build.gradle +++ b/plugins/env-files-support/build.gradle @@ -39,6 +39,7 @@ intellij { 'Docker', 'pythonid:193.6015.28', 'org.jetbrains.plugins.ruby:193.6015.9', + 'Kotlin', 'coverage', 'CSS', 'java-i18n', diff --git a/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaCompletionConfidence.java b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaCompletionConfidence.java new file mode 100644 index 000000000000..704af01ed376 --- /dev/null +++ b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaCompletionConfidence.java @@ -0,0 +1,25 @@ +package ru.adelf.idea.dotenv.java; + +import com.intellij.codeInsight.completion.CompletionConfidence; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiLiteralExpression; +import com.intellij.util.ThreeState; +import org.jetbrains.annotations.NotNull; + +public class JavaCompletionConfidence extends CompletionConfidence { + @NotNull + @Override + public ThreeState shouldSkipAutopopup(@NotNull PsiElement contextElement, @NotNull PsiFile psiFile, int offset) { + PsiElement literal = contextElement.getContext(); + if(!(literal instanceof PsiLiteralExpression)) { + return ThreeState.UNSURE; + } + + if(JavaPsiHelper.isEnvStringLiteral((PsiLiteralExpression) literal)) { + return ThreeState.NO; + } + + return ThreeState.UNSURE; + } +} \ No newline at end of file diff --git a/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaEnvCompletionContributor.java b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaEnvCompletionContributor.java new file mode 100644 index 000000000000..9db2d2f85efb --- /dev/null +++ b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaEnvCompletionContributor.java @@ -0,0 +1,76 @@ +package ru.adelf.idea.dotenv.java; + +import com.intellij.codeInsight.completion.CompletionParameters; +import com.intellij.codeInsight.completion.CompletionProvider; +import com.intellij.codeInsight.completion.CompletionResultSet; +import com.intellij.codeInsight.completion.CompletionType; +import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.editor.Editor; +import com.intellij.patterns.PlatformPatterns; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiLiteralExpression; +import com.intellij.util.ProcessingContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import ru.adelf.idea.dotenv.api.EnvironmentVariablesApi; +import ru.adelf.idea.dotenv.common.BaseEnvCompletionProvider; + +public class JavaEnvCompletionContributor extends BaseEnvCompletionProvider implements GotoDeclarationHandler { + public JavaEnvCompletionContributor() { + extend(CompletionType.BASIC, PlatformPatterns.psiElement().withParent(PsiLiteralExpression.class), new CompletionProvider() { + @Override + protected void addCompletions(@NotNull CompletionParameters completionParameters, @NotNull ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) { + PsiElement psiElement = completionParameters.getOriginalPosition(); + if(psiElement == null || getStringLiteral(psiElement) == null) { + return; + } + + fillCompletionResultSet(completionResultSet, psiElement.getProject()); + } + }); + } + + @Nullable + @Override + public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement psiElement, int i, Editor editor) { + if(psiElement == null) { + return PsiElement.EMPTY_ARRAY; + } + + PsiLiteralExpression stringLiteral = getStringLiteral(psiElement); + + if(stringLiteral == null) { + return PsiElement.EMPTY_ARRAY; + } + + Object value = stringLiteral.getValue(); + + if (!(value instanceof String)) { + return PsiElement.EMPTY_ARRAY; + } + + return EnvironmentVariablesApi.getKeyDeclarations(psiElement.getProject(), (String)value); + } + + @Nullable + private PsiLiteralExpression getStringLiteral(@NotNull PsiElement psiElement) { + PsiElement parent = psiElement.getParent(); + + if(!(parent instanceof PsiLiteralExpression)) { + return null; + } + + if(!JavaPsiHelper.isEnvStringLiteral((PsiLiteralExpression) parent)) { + return null; + } + + return (PsiLiteralExpression) parent; + } + + @Nullable + @Override + public String getActionText(@NotNull DataContext dataContext) { + return null; + } +} diff --git a/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaEnvironmentCallsVisitor.java b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaEnvironmentCallsVisitor.java new file mode 100644 index 000000000000..e10d9cdef836 --- /dev/null +++ b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaEnvironmentCallsVisitor.java @@ -0,0 +1,39 @@ +package ru.adelf.idea.dotenv.java; + +import com.intellij.psi.JavaRecursiveElementWalkingVisitor; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiLiteralExpression; +import com.intellij.psi.PsiMethodCallExpression; +import org.jetbrains.annotations.NotNull; +import ru.adelf.idea.dotenv.models.KeyUsagePsiElement; + +import java.util.Collection; +import java.util.HashSet; + +class JavaEnvironmentCallsVisitor extends JavaRecursiveElementWalkingVisitor { + final private Collection collectedItems = new HashSet<>(); + + @Override + public void visitMethodCallExpression(PsiMethodCallExpression expression) { + if (JavaPsiHelper.isEnvMethodCall(expression)) { + PsiElement[] parameters = expression.getArgumentList().getExpressions(); + + if (parameters.length == 0) return; + + if (!(parameters[0] instanceof PsiLiteralExpression)) return; + + Object value = ((PsiLiteralExpression) parameters[0]).getValue(); + + if (value instanceof String) { + collectedItems.add(new KeyUsagePsiElement((String) value, parameters[0])); + } + } + + super.visitMethodCallExpression(expression); + } + + @NotNull + Collection getCollectedItems() { + return collectedItems; + } +} diff --git a/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaEnvironmentVariablesUsagesProvider.java b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaEnvironmentVariablesUsagesProvider.java new file mode 100644 index 000000000000..d45a39e7c63e --- /dev/null +++ b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaEnvironmentVariablesUsagesProvider.java @@ -0,0 +1,32 @@ +package ru.adelf.idea.dotenv.java; + +import com.intellij.ide.highlighter.JavaFileType; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiJavaFile; +import org.jetbrains.annotations.NotNull; +import ru.adelf.idea.dotenv.api.EnvironmentVariablesUsagesProvider; +import ru.adelf.idea.dotenv.models.KeyUsagePsiElement; + +import java.util.Collection; +import java.util.Collections; + +public class JavaEnvironmentVariablesUsagesProvider implements EnvironmentVariablesUsagesProvider { + @Override + public boolean acceptFile(VirtualFile file) { + return file.getFileType().equals(JavaFileType.INSTANCE); + } + + @NotNull + @Override + public Collection getUsages(PsiFile psiFile) { + if (psiFile instanceof PsiJavaFile) { + JavaEnvironmentCallsVisitor visitor = new JavaEnvironmentCallsVisitor(); + psiFile.acceptChildren(visitor); + + return visitor.getCollectedItems(); + } + + return Collections.emptyList(); + } +} diff --git a/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaPsiHelper.java b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaPsiHelper.java new file mode 100644 index 000000000000..5994e099e726 --- /dev/null +++ b/plugins/env-files-support/src/main/java/ru/adelf/idea/dotenv/java/JavaPsiHelper.java @@ -0,0 +1,137 @@ +package ru.adelf.idea.dotenv.java; + +import com.intellij.psi.*; +import com.jetbrains.php.lang.psi.elements.ArrayAccessExpression; +import com.jetbrains.php.lang.psi.elements.PhpPsiElement; +import com.jetbrains.php.lang.psi.elements.Variable; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +class JavaPsiHelper { + public static final List ARRAY_NAMES = Arrays.asList("_ENV", "_SERVER"); + + /** + * Checks that this element environment string + * + * @param literal Checking psi element + */ + static boolean isEnvStringLiteral(PsiLiteralExpression literal) { + PsiElement parent = literal.getParent(); + + if (parent instanceof PsiExpressionList) { + PsiExpression[] expressions = ((PsiExpressionList) parent).getExpressions(); + if (expressions.length < 1) return false; + + if (expressions[0] != literal) return false; + + PsiElement methodCall = parent.getParent(); + + if (!(methodCall instanceof PsiMethodCallExpression)) return false; + + return isEnvMethodCall((PsiMethodCallExpression) methodCall); + } + + return false; + } + + /** + * Checks whether this function reference is reference for env functions, like env or getenv + * + * @param methodCallExpression Checking reference + * @return true if condition filled + */ + static boolean isEnvMethodCall(PsiMethodCallExpression methodCallExpression) { + PsiElement nameElement = methodCallExpression.getMethodExpression().getReferenceNameElement(); + + if (nameElement == null) { + return false; + } + + String methodName = nameElement.getText(); + + if (isDirectMethodCall(methodName)) { + return true; + } + + List classNames = getClassNames(methodName); + + if (classNames == null) { + return false; + } + + for (ResolveResult result : methodCallExpression.getMethodExpression().multiResolve(true)) { + if (result.getElement() instanceof PsiMethod) { + PsiClass psiClass = ((PsiMethod) result.getElement()).getContainingClass(); + + if (psiClass != null && classNames.contains(psiClass.getName())) { + return true; + } + } + } + + return false; + } + + private static boolean isDirectMethodCall(String methodName) { + return methodName.equals("getenv") || methodName.equals("getEnv"); + } + + @Nullable + private static List getClassNames(String methodName) { + switch (methodName) { + case "get": + return Arrays.asList("Dotenv", "DotEnv"); + case "getProperty": + return Collections.singletonList("System"); + } + + return null; + } + + /** + * Checks whether this array access is environment call + * + * @param arrayAccess Checking array + * @return true if condition filled + */ + static boolean isEnvArrayCall(ArrayAccessExpression arrayAccess) { + PhpPsiElement variable = arrayAccess.getValue(); + + if (!(variable instanceof Variable)) return false; + + return (variable.getName() != null && ARRAY_NAMES.contains(variable.getName())); + } + +/* @SuppressWarnings("SameParameterValue") + private static boolean isFunctionParameter(PsiElement psiElement, int parameterIndex, List functions) { + PsiElement variableContext = psiElement.getContext(); + if(!(variableContext instanceof ParameterList)) { + return false; + } else { + ParameterList parameterList = (ParameterList) variableContext; + PsiElement context = parameterList.getContext(); + if(!(context instanceof FunctionReference)) { + return false; + } else { + FunctionReference methodReference = (FunctionReference) context; + String name = methodReference.getName(); + + return (name != null && functions.contains(name) && getParameterIndex(parameterList, psiElement) == parameterIndex); + } + } + }*/ + +/* private static int getParameterIndex(ParameterList parameterList, PsiElement parameter) { + PsiElement[] parameters = parameterList.getParameters(); + for(int i = 0; i < parameters.length; i = i + 1) { + if(parameters[i].equals(parameter)) { + return i; + } + } + + return -1; + }*/ +} diff --git a/plugins/env-files-support/src/main/resources/META-INF/java.xml b/plugins/env-files-support/src/main/resources/META-INF/java.xml new file mode 100644 index 000000000000..b2cb4dd88683 --- /dev/null +++ b/plugins/env-files-support/src/main/resources/META-INF/java.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/env-files-support/src/main/resources/META-INF/plugin.xml b/plugins/env-files-support/src/main/resources/META-INF/plugin.xml index c3fac127e46e..25001be6641e 100644 --- a/plugins/env-files-support/src/main/resources/META-INF/plugin.xml +++ b/plugins/env-files-support/src/main/resources/META-INF/plugin.xml @@ -65,6 +65,7 @@ com.intellij.modules.platform + com.intellij.java com.jetbrains.php com.intellij.modules.ruby com.intellij.modules.python diff --git a/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/usages/JavaUsagesTest.java b/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/usages/JavaUsagesTest.java new file mode 100644 index 000000000000..ef47ff030746 --- /dev/null +++ b/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/usages/JavaUsagesTest.java @@ -0,0 +1,13 @@ +package ru.adelf.idea.dotenv.tests.usages; + +public class JavaUsagesTest extends BaseUsagesTest { + @Override + public void setUp() throws Exception { + super.setUp(); + myFixture.copyDirectoryToProject("java", "src/java"); + } + + public void testGetEnvUsages() { + assertUsagesContains("JAVA_GET_ENV"); + } +} diff --git a/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/usages/fixtures/java/System.java b/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/usages/fixtures/java/System.java new file mode 100644 index 000000000000..f01dd714a64f --- /dev/null +++ b/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/usages/fixtures/java/System.java @@ -0,0 +1,11 @@ +package ru.adelf.idea.dotenv.tests.usages.fixtures.java; + +public class System { + public static String getenv(String key){ + return ""; + } + + public static String getProperty(String key){ + return ""; + } +} diff --git a/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/usages/fixtures/java/Usages.java b/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/usages/fixtures/java/Usages.java new file mode 100644 index 000000000000..31963f479a57 --- /dev/null +++ b/plugins/env-files-support/src/test/java/ru/adelf/idea/dotenv/tests/usages/fixtures/java/Usages.java @@ -0,0 +1,7 @@ +package ru.adelf.idea.dotenv.tests.usages.fixtures.java; + +public class Usages { + public static void getEnv() { + System.getenv("JAVA_GET_ENV"); + } +}