diff --git a/plugins/gradle/java/resources/META-INF/gradle-groovy-integration.xml b/plugins/gradle/java/resources/META-INF/gradle-groovy-integration.xml index 51ec7a78ce1c..22dce1242e39 100644 --- a/plugins/gradle/java/resources/META-INF/gradle-groovy-integration.xml +++ b/plugins/gradle/java/resources/META-INF/gradle-groovy-integration.xml @@ -35,8 +35,6 @@ - @@ -61,6 +59,7 @@ + diff --git a/plugins/gradle/java/src/codeInsight/ConfigurationsCompletionContributor.kt b/plugins/gradle/java/src/codeInsight/ConfigurationsCompletionContributor.kt deleted file mode 100644 index 312517f9192a..000000000000 --- a/plugins/gradle/java/src/codeInsight/ConfigurationsCompletionContributor.kt +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package org.jetbrains.plugins.gradle.codeInsight - -import com.intellij.codeInsight.completion.* -import com.intellij.codeInsight.lookup.LookupElementBuilder -import com.intellij.patterns.PlatformPatterns.psiElement -import com.intellij.psi.PsiElement -import com.intellij.util.ProcessingContext -import icons.GradleIcons -import org.jetbrains.plugins.gradle.service.resolve.GradleCommonClassNames.* -import org.jetbrains.plugins.gradle.service.resolve.GradleExtensionsContributor -import org.jetbrains.plugins.groovy.dsl.holders.NonCodeMembersHolder -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression -import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightVariable -import org.jetbrains.plugins.groovy.lang.psi.patterns.groovyClosure -import org.jetbrains.plugins.groovy.lang.psi.patterns.psiMethod -import java.lang.Double.MAX_VALUE - -/** - * @author Vladislav.Soroka - */ -class ConfigurationsCompletionContributor : AbstractGradleCompletionContributor() { - - class ConfigurationsCompletionProvider(val isScriptClasspath: Boolean = false) : CompletionProvider() { - - override fun addCompletions(params: CompletionParameters, - context: ProcessingContext, - result: CompletionResultSet) { - val position = params.position - val extensionsData = GradleExtensionsContributor.getExtensionsFor(position) ?: return - for (configuration in extensionsData.configurations.values) { - if (isScriptClasspath != configuration.scriptClasspath) continue - - val confVar = object : GrLightVariable(position.manager, configuration.name, GRADLE_API_CONFIGURATION, position) { - override fun getNavigationElement(): PsiElement { - val navigationElement = super.getNavigationElement() - val description = if (isScriptClasspath && configuration.description == null) DEFAULT_SCRIPT_CLASSPATH_DESCRIPTION else configuration.description - navigationElement.putUserData(NonCodeMembersHolder.DOCUMENTATION, description) - return navigationElement - } - } - val elementBuilder = LookupElementBuilder.create(confVar, configuration.name) - .withIcon(GradleIcons.Gradle) - .withTypeText("Configuration") - result.addElement(PrioritizedLookupElement.withPriority(elementBuilder, MAX_VALUE)) - } - } - } - - init { - extend(CompletionType.BASIC, PROJECT_DEPENDENCIES_PLACE_PATTERN, ConfigurationsCompletionProvider()) - extend(CompletionType.BASIC, SCRIPT_DEPENDENCIES_PLACE_PATTERN, ConfigurationsCompletionProvider(true)) - } - - companion object { - - private const val DEFAULT_SCRIPT_CLASSPATH_DESCRIPTION = "The script classpath configuration used to compile and execute a build script. " + - "This classpath is also used to load the plugins which the build script uses." - - private val PROJECT_DEPENDENCIES_PLACE_PATTERN = psiElement() - .and(GRADLE_FILE_PATTERN) - .withParent(GrReferenceExpression::class.java) - .withAncestor(4, groovyClosure().inMethod(psiMethod(GRADLE_API_PROJECT, "dependencies"))) - - private val SCRIPT_DEPENDENCIES_PLACE_PATTERN = psiElement() - .and(GRADLE_FILE_PATTERN) - .withParent(GrReferenceExpression::class.java) - .withAncestor(4, groovyClosure().inMethod(psiMethod(GRADLE_API_SCRIPT_HANDLER, "dependencies"))) - } -} \ No newline at end of file diff --git a/plugins/gradle/java/src/service/resolve/GradleBuildScriptContributor.kt b/plugins/gradle/java/src/service/resolve/GradleBuildScriptContributor.kt index bddec09dcdb7..eb0a60fbaed2 100644 --- a/plugins/gradle/java/src/service/resolve/GradleBuildScriptContributor.kt +++ b/plugins/gradle/java/src/service/resolve/GradleBuildScriptContributor.kt @@ -1,6 +1,7 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.plugins.gradle.service.resolve +import com.intellij.util.ProcessingContext import groovy.lang.Closure import org.jetbrains.plugins.gradle.service.resolve.GradleCommonClassNames.GRADLE_API_PROJECT import org.jetbrains.plugins.gradle.service.resolve.GradleCommonClassNames.GRADLE_API_SCRIPT_HANDLER @@ -16,12 +17,17 @@ import org.jetbrains.plugins.groovy.lang.resolve.delegatesTo.DelegatesToInfo */ class GradleBuildScriptContributor : GradleMethodContextContributor { companion object { - val buildscriptClosure: GroovyClosurePattern = groovyClosure().inMethod(psiMethod(GRADLE_API_PROJECT, "buildscript")) + val buildscriptClosure: GroovyClosurePattern = groovyClosure().inMethod( + psiMethod(GRADLE_API_PROJECT, "buildscript") + ).inMethodResult(saveProjectType) } override fun getDelegatesToInfo(closure: GrClosableBlock): DelegatesToInfo? { - if (buildscriptClosure.accepts(closure)) { - return DelegatesToInfo(TypesUtil.createType(GRADLE_API_SCRIPT_HANDLER, closure), Closure.DELEGATE_FIRST) + val context = ProcessingContext() + if (buildscriptClosure.accepts(closure, context)) { + val scriptHandler = TypesUtil.createType(GRADLE_API_SCRIPT_HANDLER, closure) + val delegate = context.get(projectTypeKey)?.setType(scriptHandler, true) ?: scriptHandler + return DelegatesToInfo(delegate, Closure.DELEGATE_FIRST) } return null } diff --git a/plugins/gradle/java/src/service/resolve/GradleDependenciesContributor.kt b/plugins/gradle/java/src/service/resolve/GradleDependenciesContributor.kt index 8e898b153241..e429003b7008 100644 --- a/plugins/gradle/java/src/service/resolve/GradleDependenciesContributor.kt +++ b/plugins/gradle/java/src/service/resolve/GradleDependenciesContributor.kt @@ -1,25 +1,15 @@ // Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package org.jetbrains.plugins.gradle.service.resolve -import com.intellij.patterns.PsiJavaPatterns.psiElement import com.intellij.patterns.StandardPatterns.or -import com.intellij.psi.JavaPsiFacade -import com.intellij.psi.PsiElement -import com.intellij.psi.ResolveState -import com.intellij.psi.scope.ElementClassHint -import com.intellij.psi.scope.PsiScopeProcessor +import com.intellij.util.ProcessingContext import groovy.lang.Closure import org.jetbrains.plugins.gradle.service.resolve.GradleCommonClassNames.* -import org.jetbrains.plugins.gradle.service.resolve.GradleResolverUtil.canBeMethodOf import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock -import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression -import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil -import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder import org.jetbrains.plugins.groovy.lang.psi.patterns.GroovyClosurePattern import org.jetbrains.plugins.groovy.lang.psi.patterns.groovyClosure import org.jetbrains.plugins.groovy.lang.psi.patterns.psiMethod -import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil import org.jetbrains.plugins.groovy.lang.resolve.delegatesTo.DelegatesToInfo /** @@ -31,7 +21,7 @@ class GradleDependenciesContributor : GradleMethodContextContributor { val dependenciesClosure: GroovyClosurePattern = groovyClosure().inMethod(or( psiMethod(GRADLE_API_PROJECT, "dependencies"), psiMethod(GRADLE_API_SCRIPT_HANDLER, "dependencies") - )) + )).inMethodResult(saveProjectType) val dependencyClosure: GroovyClosurePattern = groovyClosure().inMethod(psiMethod(GRADLE_API_DEPENDENCY_HANDLER, "add")) val moduleClosure: GroovyClosurePattern = groovyClosure().inMethod(psiMethod(GRADLE_API_DEPENDENCY_HANDLER, "module")) @@ -44,8 +34,13 @@ class GradleDependenciesContributor : GradleMethodContextContributor { } override fun getDelegatesToInfo(closure: GrClosableBlock): DelegatesToInfo? { + val context = ProcessingContext() + if (dependenciesClosure.accepts(closure, context)) { + val dependencyHandler = TypesUtil.createType(GRADLE_API_DEPENDENCY_HANDLER, closure) + val delegate = context.get(projectTypeKey)?.setType(dependencyHandler) ?: dependencyHandler + return DelegatesToInfo(delegate, Closure.DELEGATE_FIRST) + } val fqn = when { - dependenciesClosure.accepts(closure) -> GRADLE_API_DEPENDENCY_HANDLER dependencyClosure.accepts(closure) -> GRADLE_API_ARTIFACTS_MODULE_DEPENDENCY moduleClosure.accepts(closure) -> GRADLE_API_ARTIFACTS_CLIENT_MODULE_DEPENDENCY componentsClosure.accepts(closure) -> GRADLE_API_COMPONENT_METADATA_HANDLER @@ -55,30 +50,4 @@ class GradleDependenciesContributor : GradleMethodContextContributor { } return DelegatesToInfo(TypesUtil.createType(fqn, closure), Closure.DELEGATE_FIRST) } - - override fun process(methodCallInfo: List, processor: PsiScopeProcessor, state: ResolveState, place: PsiElement): Boolean { - val groovyPsiManager = GroovyPsiManager.getInstance(place.project) - val methodName = methodCallInfo.firstOrNull() ?: return true - - val classHint = processor.getHint(ElementClassHint.KEY) - val shouldProcessMethods = ResolveUtil.shouldProcessMethods(classHint) - if (shouldProcessMethods && place is GrReferenceExpression && psiElement().inside(dependenciesClosure).accepts(place)) { - if (methodCallInfo.size == 2) { - val resolveScope = place.getResolveScope() - val psiClass = JavaPsiFacade.getInstance(place.project).findClass(GRADLE_API_DEPENDENCY_HANDLER, resolveScope) ?: return true - if (canBeMethodOf(methodName, psiClass)) { - return true - } - - val returnClass = groovyPsiManager.createTypeByFQClassName(GRADLE_API_ARTIFACTS_DEPENDENCY, resolveScope) - val wrappedBase = GrLightMethodBuilder(place.manager, methodName).apply { - returnType = returnClass - containingClass = psiClass - } - wrappedBase.addParameter("dependencyNotation", TypesUtil.getJavaLangObject(place).createArrayType()) - if (!processor.execute(wrappedBase, state)) return false - } - } - return true - } -} \ No newline at end of file +} diff --git a/plugins/gradle/java/src/service/resolve/GradleDependencyHandlerContributor.kt b/plugins/gradle/java/src/service/resolve/GradleDependencyHandlerContributor.kt new file mode 100644 index 000000000000..faf91bf2cf04 --- /dev/null +++ b/plugins/gradle/java/src/service/resolve/GradleDependencyHandlerContributor.kt @@ -0,0 +1,60 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.plugins.gradle.service.resolve + +import com.intellij.psi.* +import com.intellij.psi.scope.PsiScopeProcessor +import icons.GradleIcons +import org.jetbrains.plugins.gradle.service.resolve.GradleCommonClassNames.GRADLE_API_DEPENDENCY_HANDLER +import org.jetbrains.plugins.gradle.settings.GradleExtensionsSettings.GradleConfiguration +import org.jetbrains.plugins.gradle.util.GradleBundle +import org.jetbrains.plugins.groovy.dsl.holders.NonCodeMembersHolder +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil +import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrLightMethodBuilder +import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor +import org.jetbrains.plugins.groovy.lang.resolve.getName +import org.jetbrains.plugins.groovy.lang.resolve.shouldProcessMethods + +class GradleDependencyHandlerContributor : NonCodeMembersContributor() { + + override fun getParentClassName(): String? = GRADLE_API_DEPENDENCY_HANDLER + + override fun processDynamicElements(qualifierType: PsiType, + clazz: PsiClass?, + processor: PsiScopeProcessor, + place: PsiElement, + state: ResolveState) { + if (qualifierType !is GradleProjectAwareType) return + + if (clazz == null) return + if (!processor.shouldProcessMethods()) return + + val data = GradleExtensionsContributor.getExtensionsFor(place) ?: return + val methodName = processor.getName(state) + val manager = place.manager + val objectVarargType = PsiEllipsisType(TypesUtil.getJavaLangObject(place)) + + val configurations = if (methodName == null) data.configurations.values else listOf(data.configurations[methodName] ?: return) + for (configuration in configurations) { + if (configuration.scriptClasspath != qualifierType.buildscript) continue + val configurationName = configuration.name ?: continue + val method = GrLightMethodBuilder(manager, configurationName).apply { + methodKind = GradleArtifactHandlerContributor.ourMethodKind + containingClass = clazz + returnType = null + addParameter("dependencyNotation", objectVarargType) + setBaseIcon(GradleIcons.Gradle) + putUserData(NonCodeMembersHolder.DOCUMENTATION, configuration.getDescription()) + } + if (!processor.execute(method, state)) return + } + } + + private fun GradleConfiguration.getDescription(): String? { + if (description == null && scriptClasspath && name == "classpath") { + return GradleBundle.message("gradle.codeInsigt.buildscript.classpath.description") + } + else { + return description + } + } +} diff --git a/plugins/gradle/java/src/service/resolve/GradleProjectAwareType.kt b/plugins/gradle/java/src/service/resolve/GradleProjectAwareType.kt index f15c25a4f5a3..b64507220e43 100644 --- a/plugins/gradle/java/src/service/resolve/GradleProjectAwareType.kt +++ b/plugins/gradle/java/src/service/resolve/GradleProjectAwareType.kt @@ -8,8 +8,9 @@ import com.intellij.psi.PsiType import com.intellij.psi.search.GlobalSearchScope // TODO track exact project id to obtain project extension data -class GradleProjectAwareType( - private val delegate: PsiClassType +class GradleProjectAwareType ( + private val delegate: PsiClassType, + val buildscript: Boolean ) : PsiClassType(LanguageLevel.HIGHEST) { override fun isValid(): Boolean = delegate.isValid @@ -17,7 +18,7 @@ class GradleProjectAwareType( override fun resolve(): PsiClass? = delegate.resolve() override fun resolveGenerics(): ClassResolveResult = delegate.resolveGenerics() override fun getParameters(): Array = delegate.parameters - override fun rawType(): PsiClassType = GradleProjectAwareType(delegate.rawType()) + override fun rawType(): PsiClassType = GradleProjectAwareType(delegate.rawType(), buildscript) override fun getClassName(): String = delegate.className override fun getCanonicalText(): String = delegate.canonicalText @@ -27,5 +28,11 @@ class GradleProjectAwareType( override fun getLanguageLevel(): LanguageLevel = delegate.languageLevel override fun setLanguageLevel(languageLevel: LanguageLevel): PsiClassType = error("must not be called") - fun setType(delegate: PsiClassType): GradleProjectAwareType = GradleProjectAwareType(delegate) + fun setType(delegate: PsiClassType): GradleProjectAwareType { + return GradleProjectAwareType(delegate, buildscript) + } + + fun setType(delegate: PsiClassType, buildscript: Boolean): GradleProjectAwareType { + return GradleProjectAwareType(delegate, buildscript) + } } diff --git a/plugins/gradle/java/src/service/resolve/GradleProjectCallTypeCalculator.kt b/plugins/gradle/java/src/service/resolve/GradleProjectCallTypeCalculator.kt index 815e17561d94..f5631956df7c 100644 --- a/plugins/gradle/java/src/service/resolve/GradleProjectCallTypeCalculator.kt +++ b/plugins/gradle/java/src/service/resolve/GradleProjectCallTypeCalculator.kt @@ -14,7 +14,8 @@ class GradleProjectCallTypeCalculator : GrCallTypeCalculator { private val methodNames = setOf( "getProject", "getArtifacts", - "getTasks" + "getTasks", + "getDependencies" ) override fun getType(receiver: PsiType?, method: PsiMethod, arguments: Arguments?, context: PsiElement): PsiType? { diff --git a/plugins/gradle/java/src/service/resolve/GradleScriptMembersContributor.kt b/plugins/gradle/java/src/service/resolve/GradleScriptMembersContributor.kt index 9bc09e2cf1fb..f930d7959872 100644 --- a/plugins/gradle/java/src/service/resolve/GradleScriptMembersContributor.kt +++ b/plugins/gradle/java/src/service/resolve/GradleScriptMembersContributor.kt @@ -21,7 +21,7 @@ class GradleScriptMembersContributor : NonCodeMembersContributor() { place: PsiElement, state: ResolveState) { if (aClass == null || !aClass.isResolvedInGradleScript()) return - val delegate = GradleProjectAwareType(createType(GRADLE_API_PROJECT, place.containingFile)) + val delegate = GradleProjectAwareType(createType(GRADLE_API_PROJECT, place.containingFile), false) delegate.processReceiverType(processor, state, place) } } diff --git a/plugins/gradle/java/testSources/dsl/GradleDependenciesTest.groovy b/plugins/gradle/java/testSources/dsl/GradleDependenciesTest.groovy index e626d0c33920..167cc01eb2e1 100644 --- a/plugins/gradle/java/testSources/dsl/GradleDependenciesTest.groovy +++ b/plugins/gradle/java/testSources/dsl/GradleDependenciesTest.groovy @@ -16,12 +16,12 @@ class GradleDependenciesTest extends GradleHighlightingBaseTest implements Resol @Override protected List getParentCalls() { - return super.getParentCalls() + 'buildscript' + return [] } @Test void dependenciesTest() { - importProject("") + importProject("apply plugin: 'java'") new RunAll().append { 'dependencies delegate'() } append { @@ -38,6 +38,14 @@ class GradleDependenciesTest extends GradleHighlightingBaseTest implements Resol 'modules delegate'() } append { 'modules module delegate'() + } append { + 'classpath configuration'() + } append { + 'compile configuration'() + } append { + 'buildscript classpath configuration'() + } append { + 'buildscript compile configuration'() } run() } @@ -96,4 +104,34 @@ class GradleDependenciesTest extends GradleHighlightingBaseTest implements Resol closureDelegateTest(GRADLE_API_COMPONENT_MODULE_METADATA_DETAILS, 1) } } + + void 'classpath configuration'() { + doTest('dependencies { classpath("hi") }') { + resolveTest(null) + } + } + + void 'compile configuration'() { + doTest('dependencies { compile("hi") }') { + methodTest(resolveTest(PsiMethod), "compile", GRADLE_API_DEPENDENCY_HANDLER) + } + } + + void 'compile confiduration via property'() { + doTest('dependencies.testCompile("hi")') { + methodTest(resolveTest(PsiMethod), "testCompile", GRADLE_API_DEPENDENCY_HANDLER) + } + } + + void 'buildscript classpath configuration'() { + doTest('buildscript { dependencies { classpath("hi") } }') { + methodTest(resolveTest(PsiMethod), "classpath", GRADLE_API_DEPENDENCY_HANDLER) + } + } + + void 'buildscript compile configuration'() { + doTest('buildscript { dependencies { compile("hi") } }') { + resolveTest(null) + } + } } diff --git a/plugins/gradle/resources/i18n/GradleBundle.properties b/plugins/gradle/resources/i18n/GradleBundle.properties index 0e81960856c4..e109a1fbcb58 100644 --- a/plugins/gradle/resources/i18n/GradleBundle.properties +++ b/plugins/gradle/resources/i18n/GradleBundle.properties @@ -46,6 +46,9 @@ gradle.codeInsight.action.apply_plugin.popup.title=Choose plugin gradle.codeInsight.action.add_maven_dependency.text=Add Maven artifact dependency gradle.codeInsight.action.add_maven_dependency.description=Add selected Maven artifact dependency to the project +gradle.codeInsigt.buildscript.classpath.description=The script classpath configuration used to compile and execute a build script. \ +This classpath is also used to load the plugins which the build script uses. + gradle.runner=Runner gradle.runner.toggle.tree.text.action.name=Toggle tasks executions/text mode gradle.preferred_test_runner.CHOOSE_PER_TEST=Choose per test