From 368636d2415c86f59f065631b2a29014487bdcf7 Mon Sep 17 00:00:00 2001 From: "Vladislav.Soroka" Date: Fri, 30 Aug 2013 16:43:46 +0400 Subject: [PATCH] IDEA-50450 Gradle code insight. 'configurations' resolving added --- plugins/gradle/src/META-INF/plugin.xml | 2 + .../GradleConfigurationsContributor.java | 86 ++++++++++++++ ...nfigurationsNonCodeMembersContributor.java | 105 ++++++++++++++++++ .../GradleDependenciesContributor.java | 22 +--- .../service/resolve/GradleResolverUtil.java | 44 ++++++++ .../resolve/GradleRootContributor.java | 15 ++- 6 files changed, 255 insertions(+), 19 deletions(-) create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleConfigurationsContributor.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleConfigurationsNonCodeMembersContributor.java create mode 100644 plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleResolverUtil.java diff --git a/plugins/gradle/src/META-INF/plugin.xml b/plugins/gradle/src/META-INF/plugin.xml index acc6f3a3d0a2..5f8a4ec9a260 100644 --- a/plugins/gradle/src/META-INF/plugin.xml +++ b/plugins/gradle/src/META-INF/plugin.xml @@ -31,6 +31,7 @@ + @@ -72,6 +73,7 @@ + diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleConfigurationsContributor.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleConfigurationsContributor.java new file mode 100644 index 000000000000..61c3e1848183 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleConfigurationsContributor.java @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.gradle.service.resolve; + +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiVariable; +import com.intellij.psi.ResolveState; +import com.intellij.psi.scope.PsiScopeProcessor; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ConfigurationContainer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager; +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrReferenceExpressionImpl; +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrImplicitVariableImpl; + +import java.util.List; + +/** + * @author Vladislav.Soroka + * @since 8/29/13 + */ +public class GradleConfigurationsContributor implements GradleMethodContextContributor { + + private static final String CONFIGURATIONS = "configurations"; + + @Override + public void process(@NotNull List methodCallInfo, + @NotNull PsiScopeProcessor processor, + @NotNull ResolveState state, + @NotNull PsiElement place) { + if (methodCallInfo.isEmpty()) { + return; + } + final String methodCall = methodCallInfo.get(0); + + PsiClass contributorClass = null; + GroovyPsiManager psiManager = GroovyPsiManager.getInstance(place.getProject()); + if (methodCallInfo.size() == 1) { + if (methodCall.startsWith(CONFIGURATIONS + '.')) { + contributorClass = psiManager.findClassWithCache(Configuration.class.getName(), place.getResolveScope()); + } + else if (CONFIGURATIONS.equals(methodCall)) { + contributorClass = psiManager.findClassWithCache(ConfigurationContainer.class.getName(), place.getResolveScope()); + if (place instanceof GrReferenceExpressionImpl) { + addImplicitConfiguration(processor, state, (GrReferenceExpressionImpl)place); + return; + } + } + } + else if (methodCallInfo.size() == 2) { + if (CONFIGURATIONS.equals(methodCallInfo.get(1))) { + contributorClass = psiManager.findClassWithCache(ConfigurationContainer.class.getName(), place.getResolveScope()); + if (place instanceof GrReferenceExpressionImpl) { + addImplicitConfiguration(processor, state, (GrReferenceExpressionImpl)place); + return; + } + } + } + if (contributorClass != null) { + contributorClass.processDeclarations(processor, state, null, place); + } + } + + @SuppressWarnings("MethodMayBeStatic") + private void addImplicitConfiguration(@NotNull PsiScopeProcessor processor, + @NotNull ResolveState state, + @NotNull GrReferenceExpressionImpl expression) { + String expr = expression.getCanonicalText(); + PsiVariable myPsi = new GrImplicitVariableImpl(expression.getManager(), expr, Configuration.class.getName(), expression); + processor.execute(myPsi, state); + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleConfigurationsNonCodeMembersContributor.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleConfigurationsNonCodeMembersContributor.java new file mode 100644 index 000000000000..54de0da26200 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleConfigurationsNonCodeMembersContributor.java @@ -0,0 +1,105 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.gradle.service.resolve; + +import com.intellij.psi.*; +import com.intellij.psi.impl.light.LightElement; +import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.psi.util.PsiTreeUtil; +import groovy.lang.Closure; +import org.gradle.api.artifacts.Configuration; +import org.gradle.api.artifacts.ConfigurationContainer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrMethodCall; +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrReferenceExpressionImpl; +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrImplicitVariableImpl; +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder; +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightParameter; +import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor; + +/** + * @author Vladislav.Soroka + * @since 8/30/13 + */ +public class GradleConfigurationsNonCodeMembersContributor extends NonCodeMembersContributor { + + private static final String METHOD_GET_BY_NAME = "getByName"; + + @Override + public void processDynamicElements(@NotNull PsiType qualifierType, + PsiClass aClass, + PsiScopeProcessor processor, + PsiElement place, + ResolveState state) { + if (place == null) { + return; + } + + if (!ConfigurationContainer.class.getName().equals(aClass.getQualifiedName())) { + return; + } + + // Assuming that the method call is equivalent to calling ConfigurationContainer.getByName() + processConfigurationAddition(aClass, processor, state, place); + } + + @SuppressWarnings("MethodMayBeStatic") + private void processConfigurationAddition(@NotNull PsiClass dependencyHandlerClass, + @NotNull PsiScopeProcessor processor, + @NotNull ResolveState state, + @NotNull PsiElement place) { + + GrMethodCall call = PsiTreeUtil.getParentOfType(place, GrMethodCall.class); + if (call == null) { + // TODO replace with groovy implicit method + GrReferenceExpressionImpl expression = (GrReferenceExpressionImpl)place; + String expr = expression.getCanonicalText(); + GrImplicitVariableImpl myPsi = new GrImplicitVariableImpl(place.getManager(), expr, Configuration.class.getName(), place); + processor.execute(myPsi, state); + setNavigation(myPsi, dependencyHandlerClass, METHOD_GET_BY_NAME, 1); + return; + } + GrArgumentList args = call.getArgumentList(); + if (args == null) { + return; + } + int argsCount = GradleResolverUtil.getGrMethodArumentsCount(args); + + argsCount++; // Configuration name is delivered as an argument. + + if (argsCount == 1) { + GrLightMethodBuilder builder = new GrLightMethodBuilder(place.getManager(), METHOD_GET_BY_NAME); + PsiClassType type = PsiType.getJavaLangObject(place.getManager(), place.getResolveScope()); + builder.addParameter(new GrLightParameter("s", type, builder)); + processor.execute(builder, state); + + argsCount++; // we need method with extra argument of type Closure. + setNavigation(builder, dependencyHandlerClass, METHOD_GET_BY_NAME, argsCount); + } + } + + @SuppressWarnings("MethodMayBeStatic") + private void setNavigation(LightElement element, PsiClass dependencyHandlerClass, String methodName, int parametersCount) { + for (PsiMethod method : dependencyHandlerClass.findMethodsByName(methodName, false)) { + int methodParameterCount = method.getParameterList().getParametersCount(); + if (methodParameterCount == parametersCount && + method.getParameterList().getParameters()[methodParameterCount - 1].getType().equalsToText(Closure.class.getName())) { + element.setNavigationElement(method); + } + } + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleDependenciesContributor.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleDependenciesContributor.java index 2b980cc27c2c..06727fbe3802 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleDependenciesContributor.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleDependenciesContributor.java @@ -35,7 +35,7 @@ import java.util.List; * @since 8/14/13 12:58 PM */ public class GradleDependenciesContributor implements GradleMethodContextContributor { - + @Override public void process(@NotNull List methodCallInfo, @NotNull PsiScopeProcessor processor, @@ -50,7 +50,7 @@ public class GradleDependenciesContributor implements GradleMethodContextContrib if (i != 1) { return; } - + // Assuming that the method call is addition of new dependency into configuration. GroovyPsiManager psiManager = GroovyPsiManager.getInstance(place.getProject()); PsiClass contributorClass = psiManager.findClassWithCache(DependencyHandler.class.getName(), place.getResolveScope()); @@ -70,7 +70,7 @@ public class GradleDependenciesContributor implements GradleMethodContextContrib PsiClassType type = PsiType.getJavaLangObject(place.getManager(), place.getResolveScope()); builder.addParameter(new GrLightParameter("dependencyInfo", type, builder)); processor.execute(builder, state); - + GrMethodCall call = PsiTreeUtil.getParentOfType(place, GrMethodCall.class); if (call == null) { return; @@ -79,20 +79,8 @@ public class GradleDependenciesContributor implements GradleMethodContextContrib if (args == null) { return; } - int argsCount = 0; - boolean namedArgProcessed = false; - for (GroovyPsiElement arg : args.getAllArguments()) { - if (arg instanceof GrNamedArgument) { - if (!namedArgProcessed) { - namedArgProcessed = true; - argsCount++; - } - } - else { - argsCount++; - } - } - + + int argsCount = GradleResolverUtil.getGrMethodArumentsCount(args); argsCount++; // Configuration name is delivered as an argument. for (PsiMethod method : dependencyHandlerClass.findMethodsByName("add", false)) { diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleResolverUtil.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleResolverUtil.java new file mode 100644 index 000000000000..fb2a539ac155 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleResolverUtil.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jetbrains.plugins.gradle.service.resolve; + +import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList; +import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrNamedArgument; + +/** + * @author Vladislav.Soroka + * @since 8/30/13 + */ +public class GradleResolverUtil { + + public static int getGrMethodArumentsCount(GrArgumentList args) { + int argsCount = 0; + boolean namedArgProcessed = false; + for (GroovyPsiElement arg : args.getAllArguments()) { + if (arg instanceof GrNamedArgument) { + if (!namedArgProcessed) { + namedArgProcessed = true; + argsCount++; + } + } + else { + argsCount++; + } + } + return argsCount; + } +} diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleRootContributor.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleRootContributor.java index 2e5613f5a77e..d30ef89d6d03 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleRootContributor.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/service/resolve/GradleRootContributor.java @@ -19,12 +19,13 @@ import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.ResolveState; import com.intellij.psi.scope.PsiScopeProcessor; -import com.intellij.util.containers.Stack; +import com.intellij.util.containers.ContainerUtil; import org.gradle.api.Project; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager; import java.util.List; +import java.util.Set; /** * @author Denis Zhdanov @@ -32,12 +33,22 @@ import java.util.List; */ public class GradleRootContributor implements GradleMethodContextContributor { + private final static Set PROJECT_ACTIONS = ContainerUtil.newHashSet( + "subprojects", + "allprojects", + "beforeEvaluate", + "afterEvaluate"); + @Override public void process(@NotNull List methodCallInfo, @NotNull PsiScopeProcessor processor, @NotNull ResolveState state, @NotNull PsiElement place) { - if (methodCallInfo.size() > 1) { + if(methodCallInfo.size() > 2) { + return; + } + + if (methodCallInfo.size() == 2 && !PROJECT_ACTIONS.contains(methodCallInfo.get(1))) { return; }