KotlinWithGradleConfigurator: J2K

This commit is contained in:
Dmitry Jemerov
2016-11-10 20:19:15 +01:00
parent 11c9dff97a
commit 47c80a1ad1
4 changed files with 368 additions and 417 deletions
@@ -61,13 +61,13 @@ public class KotlinAndroidGradleModuleConfigurator extends KotlinWithGradleConfi
@Override
protected boolean addSourceSetsBlock(@NotNull GroovyFile file) {
GrClosableBlock androidBlock = getAndroidBlock(file);
return addLastExpressionInBlockIfNeeded(SOURCE_SET, getSourceSetsBlock(androidBlock));
return Companion.addLastExpressionInBlockIfNeeded(Companion.getSOURCE_SET(), Companion.getSourceSetsBlock(androidBlock));
}
@Override
protected boolean addElementsToFile(@NotNull GroovyFile groovyFile, boolean isTopLevelProjectFile, @NotNull String version) {
if (isTopLevelProjectFile) {
return addElementsToProjectFile(groovyFile, version);
return Companion.addElementsToProjectFile(groovyFile, version);
}
else {
return addElementsToModuleFile(groovyFile, version);
@@ -76,7 +76,7 @@ public class KotlinAndroidGradleModuleConfigurator extends KotlinWithGradleConfi
@NotNull
private static GrClosableBlock getAndroidBlock(@NotNull GroovyFile file) {
return getBlockOrCreate(file, "android");
return Companion.getBlockOrCreate(file, "android");
}
KotlinAndroidGradleModuleConfigurator() {
@@ -45,10 +45,10 @@ class GradleKotlinJavaFrameworkSupportProvider() : GradleFrameworkSupportProvide
val additionalRepository: String? = when {
kotlinVersion == "@snapshot@" -> {
kotlinVersion = "1.1-SNAPSHOT"
KotlinWithGradleConfigurator.SNAPSHOT_REPOSITORY
KotlinWithGradleConfigurator.SNAPSHOT_REPOSITORY_SNIPPET
}
isEap(kotlinVersion) -> {
KotlinWithGradleConfigurator.EAP_REPOSITORY
KotlinWithGradleConfigurator.EAP_REPOSITORY_SNIPPET
}
else -> {
null
@@ -60,14 +60,14 @@ public class KotlinGradleModuleConfigurator extends KotlinWithGradleConfigurator
@Override
protected boolean addSourceSetsBlock(@NotNull GroovyFile file) {
GrClosableBlock sourceSetBlock = getSourceSetsBlock(file);
return addLastExpressionInBlockIfNeeded(SOURCE_SET, sourceSetBlock);
GrClosableBlock sourceSetBlock = Companion.getSourceSetsBlock(file);
return Companion.addLastExpressionInBlockIfNeeded(Companion.getSOURCE_SET(), sourceSetBlock);
}
@Override
protected boolean addElementsToFile(@NotNull GroovyFile groovyFile, boolean isTopLevelProjectFile, @NotNull String version) {
if (!isTopLevelProjectFile) {
boolean wasModified = addElementsToProjectFile(groovyFile, version);
boolean wasModified = Companion.addElementsToProjectFile(groovyFile, version);
wasModified |= addElementsToModuleFile(groovyFile, version);
return wasModified;
}
@@ -14,483 +14,434 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.configuration;
package org.jetbrains.kotlin.idea.configuration
import com.intellij.codeInsight.CodeInsightUtilCore;
import com.intellij.ide.actions.OpenFileAction;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.DependencyScope;
import com.intellij.openapi.roots.ExternalLibraryDescriptor;
import com.intellij.openapi.roots.ModuleRootManager;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.WritingAccessProvider;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.KotlinPluginUtil;
import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion;
import org.jetbrains.plugins.gradle.util.GradleConstants;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplicationStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner;
import com.intellij.codeInsight.CodeInsightUtilCore
import com.intellij.ide.actions.OpenFileAction
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.DependencyScope
import com.intellij.openapi.roots.ExternalLibraryDescriptor
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.vfs.VfsUtil
import com.intellij.openapi.vfs.WritingAccessProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import com.intellij.psi.codeStyle.CodeStyleManager
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.idea.KotlinPluginUtil
import org.jetbrains.kotlin.idea.framework.ui.ConfigureDialogWithModulesAndVersion
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.plugins.gradle.util.GradleConstants
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplicationStatement
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner
import java.io.File
import java.util.*
import java.io.File;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
abstract class KotlinWithGradleConfigurator : KotlinProjectConfigurator {
public abstract class KotlinWithGradleConfigurator implements KotlinProjectConfigurator {
private static final String VERSION_TEMPLATE = "$VERSION$";
public static final String GROUP_ID = "org.jetbrains.kotlin";
public static final String GRADLE_PLUGIN_ID = "kotlin-gradle-plugin";
protected static final String CLASSPATH = "classpath \"" + GROUP_ID + ":" + GRADLE_PLUGIN_ID + ":$kotlin_version\"";
protected static final String SNAPSHOT_REPOSITORY = "maven {\nurl '" + ConfigureKotlinInProjectUtilsKt.SNAPSHOT_REPOSITORY.getUrl() + "'\n}";
protected static final String EAP_REPOSITORY = "maven {\nurl '" + ConfigureKotlinInProjectUtilsKt.EAP_REPOSITORY.getUrl() + "'\n}";
private static final String MAVEN_CENTRAL = "mavenCentral()\n";
private static final String JCENTER = "jcenter()\n";
public static final String LIBRARY = "compile \"org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version\"";
protected static final String SOURCE_SET = "main.java.srcDirs += 'src/main/kotlin'\n";
private static final String VERSION = String.format("ext.kotlin_version = '%s'", VERSION_TEMPLATE);
@NotNull
@Override
public ConfigureKotlinStatus getStatus(@NotNull Module module) {
override fun getStatus(module: Module): ConfigureKotlinStatus {
if (!isApplicable(module)) {
return ConfigureKotlinStatus.NON_APPLICABLE;
return ConfigureKotlinStatus.NON_APPLICABLE
}
if (ConfigureKotlinInProjectUtilsKt.hasKotlinJvmRuntimeInScope(module)) {
return ConfigureKotlinStatus.CONFIGURED;
if (hasKotlinJvmRuntimeInScope(module)) {
return ConfigureKotlinStatus.CONFIGURED
}
GroovyFile moduleGradleFile = getBuildGradleFile(module.getProject(), getModuleFilePath(module));
val moduleGradleFile = getBuildGradleFile(module.project, getModuleFilePath(module))
if (moduleGradleFile != null && !isFileConfigured(moduleGradleFile)) {
return ConfigureKotlinStatus.CAN_BE_CONFIGURED;
return ConfigureKotlinStatus.CAN_BE_CONFIGURED
}
GroovyFile projectGradleFile = getBuildGradleFile(module.getProject(), getTopLevelProjectFilePath(module.getProject()));
val projectGradleFile = getBuildGradleFile(module.project, getTopLevelProjectFilePath(module.project))
if (projectGradleFile != null && !isFileConfigured(projectGradleFile)) {
return ConfigureKotlinStatus.CAN_BE_CONFIGURED;
return ConfigureKotlinStatus.CAN_BE_CONFIGURED
}
return ConfigureKotlinStatus.BROKEN;
return ConfigureKotlinStatus.BROKEN
}
protected abstract boolean isApplicable(@NotNull Module module);
protected abstract fun isApplicable(module: Module): Boolean
private boolean isFileConfigured(GroovyFile projectGradleFile) {
String fileText = projectGradleFile.getText();
return containsDirective(fileText, getApplyPluginDirective()) && fileText.contains(LIBRARY);
private fun isFileConfigured(projectGradleFile: GroovyFile): Boolean {
val fileText = projectGradleFile.text
return containsDirective(fileText, applyPluginDirective) && fileText.contains(LIBRARY)
}
private static boolean containsDirective(@NotNull String fileText, @NotNull String directive) {
return fileText.contains(directive)
|| fileText.contains(directive.replace("\"", "'"))
|| fileText.contains(directive.replace("'", "\""));
}
@JvmSuppressWildcards
override fun configure(project: Project, excludeModules: Collection<Module>) {
val dialog = ConfigureDialogWithModulesAndVersion(project, this, excludeModules)
@Override
public void configure(@NotNull final Project project, Collection<Module> excludeModules) {
final ConfigureDialogWithModulesAndVersion dialog =
new ConfigureDialogWithModulesAndVersion(project, this, excludeModules);
dialog.show()
if (!dialog.isOK) return
dialog.show();
if (!dialog.isOK()) return;
CommandProcessor.getInstance().executeCommand(project, {
val collector = createConfigureKotlinNotificationCollector(project)
val changedFiles = HashSet<GroovyFile>()
val projectGradleFile = getBuildGradleFile(project, getTopLevelProjectFilePath(project))
if (projectGradleFile != null && canConfigureFile(projectGradleFile)) {
val isModified = changeGradleFile(projectGradleFile, true, dialog.kotlinVersion, collector)
if (isModified) {
changedFiles.add(projectGradleFile)
}
}
CommandProcessor.getInstance().executeCommand(project, new Runnable() {
@Override
public void run() {
NotificationMessageCollector collector = NotificationMessageCollectorKt.createConfigureKotlinNotificationCollector(project);
Set<GroovyFile> changedFiles = new HashSet<GroovyFile>();
GroovyFile projectGradleFile = getBuildGradleFile(project, getTopLevelProjectFilePath(project));
if (projectGradleFile != null && canConfigureFile(projectGradleFile)) {
boolean isModified = changeGradleFile(projectGradleFile, true, dialog.getKotlinVersion(), collector);
for (module in dialog.modulesToConfigure) {
val file = getBuildGradleFile(project, getModuleFilePath(module))
if (file != null && canConfigureFile(file)) {
val isModified = changeGradleFile(file, false, dialog.kotlinVersion, collector)
if (isModified) {
changedFiles.add(projectGradleFile);
changedFiles.add(file)
}
}
for (Module module : dialog.getModulesToConfigure()) {
GroovyFile file = getBuildGradleFile(project, getModuleFilePath(module));
if (file != null && canConfigureFile(file)) {
boolean isModified = changeGradleFile(file, false, dialog.getKotlinVersion(), collector);
if (isModified) {
changedFiles.add(file);
}
}
else {
showErrorMessage(project, "Cannot find build.gradle file for module " + module.getName());
}
}
for (GroovyFile file : changedFiles) {
OpenFileAction.openFile(file.getVirtualFile(), project);
}
collector.showNotification();
}
}, "Configure Kotlin", null);
}
public static void addKotlinLibraryToModule(final Module module, final DependencyScope scope, final ExternalLibraryDescriptor libraryDescriptor) {
String gradleFilePath = getModuleFilePath(module);
final GroovyFile gradleFile = getBuildGradleFile(module.getProject(), gradleFilePath);
if (gradleFile != null && canConfigureFile(gradleFile)) {
new WriteCommandAction(gradleFile.getProject()) {
@Override
protected void run(@NotNull Result result) {
String groovyScope;
switch (scope) {
case COMPILE:
groovyScope = "compile";
break;
case TEST:
if (KotlinPluginUtil.isAndroidGradleModule(module)) {
// TODO we should add testCompile or androidTestCompile
groovyScope = "compile";
}
else {
groovyScope = "testCompile";
}
break;
case RUNTIME:
groovyScope = "runtime";
break;
case PROVIDED:
groovyScope = "compile";
break;
default:
groovyScope = "compile";
}
String dependencyString = String.format(
"%s \"%s:%s:%s\"",
groovyScope, libraryDescriptor.getLibraryGroupId(), libraryDescriptor.getLibraryArtifactId(),
libraryDescriptor.getMaxVersion());
GrClosableBlock dependenciesBlock = getDependenciesBlock(gradleFile);
addLastExpressionInBlockIfNeeded(dependencyString, dependenciesBlock);
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(gradleFile);
}
}.execute();
VirtualFile virtualFile = gradleFile.getVirtualFile();
if (virtualFile != null) {
NotificationMessageCollectorKt.createConfigureKotlinNotificationCollector(gradleFile.getProject())
.addMessage(virtualFile.getPath() + " was modified")
.showNotification();
}
}
}
@Nullable
public static String getKotlinStdlibVersion(@NotNull Module module) {
String gradleFilePath = getModuleFilePath(module);
GroovyFile gradleFile = getBuildGradleFile(module.getProject(), gradleFilePath);
if (gradleFile == null) return null;
String versionProperty = "$kotlin_version";
GrClosableBlock block = getBuildScriptBlock(gradleFile);
if (block.getText().contains("ext.kotlin_version = ")) {
return versionProperty;
}
GrStatement[] dependencies = getDependenciesBlock(gradleFile).getStatements();
String stdlibArtifactPrefix = "org.jetbrains.kotlin:kotlin-stdlib:";
for (GrStatement dependency : dependencies) {
String dependencyText = dependency.getText();
int startIndex = dependencyText.indexOf(stdlibArtifactPrefix) + stdlibArtifactPrefix.length();
int endIndex = dependencyText.length() - 1;
if (startIndex != -1 && endIndex != -1) {
return dependencyText.substring(startIndex, endIndex);
}
}
return null;
}
protected static boolean addElementsToProjectFile(@NotNull GroovyFile file, @NotNull String version) {
boolean wasModified;
GrClosableBlock buildScriptBlock = getBuildScriptBlock(file);
wasModified = addFirstExpressionInBlockIfNeeded(VERSION.replace(VERSION_TEMPLATE, version), buildScriptBlock);
GrClosableBlock buildScriptRepositoriesBlock = getBuildScriptRepositoriesBlock(file);
if (ConfigureKotlinInProjectUtilsKt.isSnapshot(version)) {
wasModified |= addLastExpressionInBlockIfNeeded(SNAPSHOT_REPOSITORY, buildScriptRepositoriesBlock);
}
else if (ConfigureKotlinInProjectUtilsKt.isEap(version)) {
wasModified |= addLastExpressionInBlockIfNeeded(EAP_REPOSITORY, buildScriptRepositoriesBlock);
}
else if (!isRepositoryConfigured(buildScriptRepositoriesBlock)) {
wasModified |= addLastExpressionInBlockIfNeeded(MAVEN_CENTRAL, buildScriptRepositoriesBlock);
}
GrClosableBlock buildScriptDependenciesBlock = getBuildScriptDependenciesBlock(file);
wasModified |= addLastExpressionInBlockIfNeeded(CLASSPATH, buildScriptDependenciesBlock);
return wasModified;
}
protected boolean addElementsToModuleFile(@NotNull GroovyFile file, @NotNull String version) {
boolean wasModified = false;
if (!containsDirective(file.getText(), getApplyPluginDirective())) {
GrExpression apply = GroovyPsiElementFactory.getInstance(file.getProject()).createExpressionFromText(getApplyPluginDirective());
GrApplicationStatement applyStatement = getApplyStatement(file);
if (applyStatement != null) {
file.addAfter(apply, applyStatement);
wasModified = true;
}
else {
GrClosableBlock buildScript = getBlockByName(file, "buildscript");
if (buildScript != null) {
file.addAfter(apply, buildScript.getParent());
wasModified = true;
}
else {
GrStatement[] statements = file.getStatements();
if (statements.length > 0) {
file.addAfter(apply, statements[statements.length - 1]);
showErrorMessage(project, "Cannot find build.gradle file for module " + module.name)
}
}
for (file in changedFiles) {
OpenFileAction.openFile(file.virtualFile, project)
}
collector.showNotification()
}, "Configure Kotlin", null)
}
protected fun addElementsToModuleFile(file: GroovyFile, version: String): Boolean {
var wasModified = false
if (!containsDirective(file.text, applyPluginDirective)) {
val apply = GroovyPsiElementFactory.getInstance(file.project).createExpressionFromText(applyPluginDirective)
val applyStatement = getApplyStatement(file)
if (applyStatement != null) {
file.addAfter(apply, applyStatement)
wasModified = true
}
else {
val buildScript = getBlockByName(file, "buildscript")
if (buildScript != null) {
file.addAfter(apply, buildScript.parent)
wasModified = true
}
else {
val statements = file.statements
if (statements.size > 0) {
file.addAfter(apply, statements[statements.size - 1])
}
else {
file.addAfter(apply, file.getFirstChild());
file.addAfter(apply, file.firstChild)
}
wasModified = true;
wasModified = true
}
}
}
GrClosableBlock repositoriesBlock = getRepositoriesBlock(file);
if (ConfigureKotlinInProjectUtilsKt.isSnapshot(version)) {
wasModified |= addLastExpressionInBlockIfNeeded(SNAPSHOT_REPOSITORY, repositoriesBlock);
val repositoriesBlock = getRepositoriesBlock(file)
if (isSnapshot(version)) {
wasModified = wasModified or addLastExpressionInBlockIfNeeded(SNAPSHOT_REPOSITORY_SNIPPET, repositoriesBlock)
}
else if (ConfigureKotlinInProjectUtilsKt.isEap(version)) {
wasModified |= addLastExpressionInBlockIfNeeded(EAP_REPOSITORY, repositoriesBlock);
else if (isEap(version)) {
wasModified = wasModified or addLastExpressionInBlockIfNeeded(EAP_REPOSITORY_SNIPPET, repositoriesBlock)
}
else if (!isRepositoryConfigured(repositoriesBlock)) {
wasModified |= addLastExpressionInBlockIfNeeded(MAVEN_CENTRAL, repositoriesBlock);
wasModified = wasModified or addLastExpressionInBlockIfNeeded(MAVEN_CENTRAL, repositoriesBlock)
}
GrClosableBlock dependenciesBlock = getDependenciesBlock(file);
wasModified |= addExpressionInBlockIfNeeded(LIBRARY, dependenciesBlock, false);
val dependenciesBlock = getDependenciesBlock(file)
wasModified = wasModified or addExpressionInBlockIfNeeded(LIBRARY, dependenciesBlock, false)
wasModified |= addSourceSetsBlock(file);
wasModified = wasModified or addSourceSetsBlock(file)
return wasModified;
return wasModified
}
private static boolean isRepositoryConfigured(GrClosableBlock repositoriesBlock) {
return repositoriesBlock.getText().contains(MAVEN_CENTRAL) || repositoriesBlock.getText().contains(JCENTER);
}
protected abstract val applyPluginDirective: String
protected abstract String getApplyPluginDirective();
protected abstract fun addSourceSetsBlock(file: GroovyFile): Boolean
protected abstract boolean addSourceSetsBlock(@NotNull GroovyFile file);
protected abstract fun addElementsToFile(
groovyFile: GroovyFile,
isTopLevelProjectFile: Boolean,
version: String
): Boolean
protected abstract boolean addElementsToFile(
@NotNull GroovyFile groovyFile,
boolean isTopLevelProjectFile,
@NotNull String version
);
fun changeGradleFile(
groovyFile: GroovyFile,
isTopLevelProjectFile: Boolean,
version: String,
collector: NotificationMessageCollector
): Boolean {
val isModified = groovyFile.project.executeWriteCommand("Configure build.gradle", null) {
val isModified = addElementsToFile(groovyFile, isTopLevelProjectFile, version)
private static boolean canConfigureFile(@NotNull GroovyFile file) {
return WritingAccessProvider.isPotentiallyWritable(file.getVirtualFile(), null);
}
@Nullable
private static GroovyFile getBuildGradleFile(Project project, @Nullable String path) {
if (path == null) {
return null;
}
VirtualFile file = VfsUtil.findFileByIoFile(new File(path), true);
if (file == null) {
return null;
}
PsiFile psiFile = PsiManager.getInstance(project).findFile(file);
if (!(psiFile instanceof GroovyFile)) return null;
return (GroovyFile) psiFile;
}
@NotNull
private static String getTopLevelProjectFilePath(@NotNull Project project) {
return project.getBasePath() + "/" + GradleConstants.DEFAULT_SCRIPT_NAME;
}
@Nullable
private static String getModuleFilePath(@NotNull Module module) {
String moduleDir = new File(module.getModuleFilePath()).getParent();
File buildGradleFile = new File(moduleDir + "/" + GradleConstants.DEFAULT_SCRIPT_NAME);
if (buildGradleFile.exists()) {
return buildGradleFile.getPath();
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(groovyFile)
isModified
}
// since IDEA 145 module file is located in .idea directory
for (VirtualFile file : ModuleRootManager.getInstance(module).getContentRoots()) {
buildGradleFile = new File(file.getPath() + "/" + GradleConstants.DEFAULT_SCRIPT_NAME);
val virtualFile = groovyFile.virtualFile
if (virtualFile != null && isModified) {
collector.addMessage(virtualFile.path + " was modified")
}
return isModified
}
companion object {
private val VERSION_TEMPLATE = "\$VERSION$"
val GROUP_ID = "org.jetbrains.kotlin"
val GRADLE_PLUGIN_ID = "kotlin-gradle-plugin"
val CLASSPATH = "classpath \"$GROUP_ID:$GRADLE_PLUGIN_ID:\$kotlin_version\""
val SNAPSHOT_REPOSITORY_SNIPPET = "maven {\nurl '" + SNAPSHOT_REPOSITORY.url + "'\n}"
val EAP_REPOSITORY_SNIPPET = "maven {\nurl '" + EAP_REPOSITORY.url + "'\n}"
private val MAVEN_CENTRAL = "mavenCentral()\n"
private val JCENTER = "jcenter()\n"
val LIBRARY = "compile \"org.jetbrains.kotlin:kotlin-stdlib:\$kotlin_version\""
val SOURCE_SET = "main.java.srcDirs += 'src/main/kotlin'\n"
private val VERSION = String.format("ext.kotlin_version = '%s'", VERSION_TEMPLATE)
private fun containsDirective(fileText: String, directive: String): Boolean {
return fileText.contains(directive)
|| fileText.contains(directive.replace("\"", "'"))
|| fileText.contains(directive.replace("'", "\""))
}
fun addKotlinLibraryToModule(module: Module, scope: DependencyScope, libraryDescriptor: ExternalLibraryDescriptor) {
val gradleFilePath = getModuleFilePath(module)
val gradleFile = getBuildGradleFile(module.project, gradleFilePath)
if (gradleFile != null && canConfigureFile(gradleFile)) {
gradleFile.project.executeWriteCommand("Add Kotlin library") {
val groovyScope: String
when (scope) {
DependencyScope.COMPILE -> groovyScope = "compile"
DependencyScope.TEST -> if (KotlinPluginUtil.isAndroidGradleModule(module)) {
// TODO we should add testCompile or androidTestCompile
groovyScope = "compile"
}
else {
groovyScope = "testCompile"
}
DependencyScope.RUNTIME -> groovyScope = "runtime"
DependencyScope.PROVIDED -> groovyScope = "compile"
else -> groovyScope = "compile"
}
val dependencyString = String.format(
"%s \"%s:%s:%s\"",
groovyScope, libraryDescriptor.libraryGroupId, libraryDescriptor.libraryArtifactId,
libraryDescriptor.maxVersion)
val dependenciesBlock = getDependenciesBlock(gradleFile)
addLastExpressionInBlockIfNeeded(dependencyString, dependenciesBlock)
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(gradleFile)
}
val virtualFile = gradleFile.virtualFile
if (virtualFile != null) {
createConfigureKotlinNotificationCollector(gradleFile.project)
.addMessage(virtualFile.path + " was modified")
.showNotification()
}
}
}
fun getKotlinStdlibVersion(module: Module): String? {
val gradleFilePath = getModuleFilePath(module)
val gradleFile = getBuildGradleFile(module.project, gradleFilePath) ?: return null
val versionProperty = "\$kotlin_version"
val block = getBuildScriptBlock(gradleFile)
if (block.text.contains("ext.kotlin_version = ")) {
return versionProperty
}
val dependencies = getDependenciesBlock(gradleFile).statements
val stdlibArtifactPrefix = "org.jetbrains.kotlin:kotlin-stdlib:"
for (dependency in dependencies) {
val dependencyText = dependency.text
val startIndex = dependencyText.indexOf(stdlibArtifactPrefix) + stdlibArtifactPrefix.length
val endIndex = dependencyText.length - 1
if (startIndex != -1 && endIndex != -1) {
return dependencyText.substring(startIndex, endIndex)
}
}
return null
}
fun addElementsToProjectFile(file: GroovyFile, version: String): Boolean {
var wasModified: Boolean
val buildScriptBlock = getBuildScriptBlock(file)
wasModified = addFirstExpressionInBlockIfNeeded(VERSION.replace(VERSION_TEMPLATE, version), buildScriptBlock)
val buildScriptRepositoriesBlock = getBuildScriptRepositoriesBlock(file)
if (isSnapshot(version)) {
wasModified = wasModified or addLastExpressionInBlockIfNeeded(SNAPSHOT_REPOSITORY_SNIPPET, buildScriptRepositoriesBlock)
}
else if (isEap(version)) {
wasModified = wasModified or addLastExpressionInBlockIfNeeded(EAP_REPOSITORY_SNIPPET, buildScriptRepositoriesBlock)
}
else if (!isRepositoryConfigured(buildScriptRepositoriesBlock)) {
wasModified = wasModified or addLastExpressionInBlockIfNeeded(MAVEN_CENTRAL, buildScriptRepositoriesBlock)
}
val buildScriptDependenciesBlock = getBuildScriptDependenciesBlock(file)
wasModified = wasModified or addLastExpressionInBlockIfNeeded(CLASSPATH, buildScriptDependenciesBlock)
return wasModified
}
private fun isRepositoryConfigured(repositoriesBlock: GrClosableBlock): Boolean {
return repositoriesBlock.text.contains(MAVEN_CENTRAL) || repositoriesBlock.text.contains(JCENTER)
}
private fun canConfigureFile(file: GroovyFile): Boolean {
return WritingAccessProvider.isPotentiallyWritable(file.virtualFile, null)
}
private fun getBuildGradleFile(project: Project, path: String?): GroovyFile? {
if (path == null) {
return null
}
val file = VfsUtil.findFileByIoFile(File(path), true) ?: return null
val psiFile = PsiManager.getInstance(project).findFile(file) as? GroovyFile ?: return null
return psiFile
}
private fun getTopLevelProjectFilePath(project: Project): String {
return project.basePath + "/" + GradleConstants.DEFAULT_SCRIPT_NAME
}
private fun getModuleFilePath(module: Module): String? {
val moduleDir = File(module.moduleFilePath).parent
var buildGradleFile = File(moduleDir + "/" + GradleConstants.DEFAULT_SCRIPT_NAME)
if (buildGradleFile.exists()) {
return buildGradleFile.getPath();
return buildGradleFile.path
}
}
return null;
}
protected boolean changeGradleFile(
@NotNull final GroovyFile groovyFile,
final boolean isTopLevelProjectFile,
@NotNull final String version,
@NotNull NotificationMessageCollector collector
) {
final boolean[] isModified = {false};
new WriteCommandAction(groovyFile.getProject()) {
@Override
protected void run(@NotNull Result result) {
isModified[0] = addElementsToFile(groovyFile, isTopLevelProjectFile, version);
CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(groovyFile);
// since IDEA 145 module file is located in .idea directory
for (file in ModuleRootManager.getInstance(module).contentRoots) {
buildGradleFile = File(file.path + "/" + GradleConstants.DEFAULT_SCRIPT_NAME)
if (buildGradleFile.exists()) {
return buildGradleFile.path
}
}
}.execute();
VirtualFile virtualFile = groovyFile.getVirtualFile();
if (virtualFile != null && isModified[0]) {
collector.addMessage(virtualFile.getPath() + " was modified");
return null
}
return isModified[0];
}
@NotNull
private static GrClosableBlock getDependenciesBlock(@NotNull GrStatementOwner file) {
return getBlockOrCreate(file, "dependencies");
}
private fun getDependenciesBlock(file: GrStatementOwner): GrClosableBlock {
return getBlockOrCreate(file, "dependencies")
}
@NotNull
protected static GrClosableBlock getSourceSetsBlock(@NotNull GrStatementOwner parent) {
return getBlockOrCreate(parent, "sourceSets");
}
fun getSourceSetsBlock(parent: GrStatementOwner): GrClosableBlock {
return getBlockOrCreate(parent, "sourceSets")
}
@NotNull
private static GrClosableBlock getBuildScriptBlock(@NotNull GrStatementOwner file) {
return getBlockOrCreate(file, "buildscript");
}
private fun getBuildScriptBlock(file: GrStatementOwner): GrClosableBlock {
return getBlockOrCreate(file, "buildscript")
}
@NotNull
private static GrClosableBlock getBuildScriptDependenciesBlock(@NotNull GrStatementOwner file) {
GrClosableBlock buildScript = getBuildScriptBlock(file);
return getBlockOrCreate(buildScript, "dependencies");
}
private fun getBuildScriptDependenciesBlock(file: GrStatementOwner): GrClosableBlock {
val buildScript = getBuildScriptBlock(file)
return getBlockOrCreate(buildScript, "dependencies")
}
@NotNull
private static GrClosableBlock getBuildScriptRepositoriesBlock(@NotNull GrStatementOwner file) {
GrClosableBlock buildScript = getBuildScriptBlock(file);
return getBlockOrCreate(buildScript, "repositories");
}
private fun getBuildScriptRepositoriesBlock(file: GrStatementOwner): GrClosableBlock {
val buildScript = getBuildScriptBlock(file)
return getBlockOrCreate(buildScript, "repositories")
}
@NotNull
private static GrClosableBlock getRepositoriesBlock(@NotNull GrStatementOwner file) {
return getBlockOrCreate(file, "repositories");
}
private fun getRepositoriesBlock(file: GrStatementOwner): GrClosableBlock {
return getBlockOrCreate(file, "repositories")
}
@NotNull
protected static GrClosableBlock getBlockOrCreate(@NotNull GrStatementOwner parent, @NotNull String name) {
GrClosableBlock block = getBlockByName(parent, name);
if (block == null) {
GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(parent.getProject());
GrExpression newBlock = factory.createExpressionFromText(name + "{\n}\n");
GrStatement[] statements = parent.getStatements();
if (statements.length > 0) {
parent.addAfter(newBlock, statements[statements.length - 1]);
fun getBlockOrCreate(parent: GrStatementOwner, name: String): GrClosableBlock {
var block = getBlockByName(parent, name)
if (block == null) {
val factory = GroovyPsiElementFactory.getInstance(parent.project)
val newBlock = factory.createExpressionFromText("$name{\n}\n")
val statements = parent.statements
if (statements.size > 0) {
parent.addAfter(newBlock, statements[statements.size - 1])
}
else {
parent.addAfter(newBlock, parent.firstChild)
}
block = getBlockByName(parent, name)!!
}
return block
}
fun addLastExpressionInBlockIfNeeded(text: String, block: GrClosableBlock): Boolean {
return addExpressionInBlockIfNeeded(text, block, false)
}
private fun addFirstExpressionInBlockIfNeeded(text: String, block: GrClosableBlock): Boolean {
return addExpressionInBlockIfNeeded(text, block, true)
}
private fun getBlockByName(parent: PsiElement, name: String): GrClosableBlock? {
val allExpressions = PsiTreeUtil.getChildrenOfType(parent, GrMethodCallExpression::class.java)
if (allExpressions != null) {
for (expression in allExpressions) {
val invokedExpression = expression.invokedExpression
if (expression.closureArguments.size == 0) continue
val expressionText = invokedExpression.text
if (expressionText == name) return expression.closureArguments[0]
}
}
return null
}
private fun addExpressionInBlockIfNeeded(text: String, block: GrClosableBlock, isFirst: Boolean): Boolean {
if (block.text.contains(text)) return false
val newStatement = GroovyPsiElementFactory.getInstance(block.project).createExpressionFromText(text)
CodeStyleManager.getInstance(block.project).reformat(newStatement)
val statements = block.statements
if (!isFirst && statements.size > 0) {
val lastStatement = statements[statements.size - 1]
if (lastStatement != null) {
block.addAfter(newStatement, lastStatement)
}
}
else {
parent.addAfter(newBlock, parent.getFirstChild());
val firstChild = block.firstChild
if (firstChild != null) {
block.addAfter(newStatement, firstChild)
}
}
block = getBlockByName(parent, name);
assert block != null : "Block should be non-null because it is created";
return true
}
return block;
}
protected static boolean addLastExpressionInBlockIfNeeded(@NotNull String text, @NotNull GrClosableBlock block) {
return addExpressionInBlockIfNeeded(text, block, false);
}
private static boolean addFirstExpressionInBlockIfNeeded(@NotNull String text, @NotNull GrClosableBlock block) {
return addExpressionInBlockIfNeeded(text, block, true);
}
@Nullable
private static GrClosableBlock getBlockByName(@NotNull PsiElement parent, @NotNull String name) {
GrMethodCallExpression[] allExpressions = PsiTreeUtil.getChildrenOfType(parent, GrMethodCallExpression.class);
if (allExpressions != null) {
for (GrMethodCallExpression expression : allExpressions) {
GrExpression invokedExpression = expression.getInvokedExpression();
if (expression.getClosureArguments().length == 0) continue;
String expressionText = invokedExpression.getText();
if (expressionText.equals(name)) return expression.getClosureArguments()[0];
private fun getApplyStatement(file: GroovyFile): GrApplicationStatement? {
val applyStatement = PsiTreeUtil.getChildrenOfType(file, GrApplicationStatement::class.java) ?: return null
for (callExpression in applyStatement) {
val invokedExpression = callExpression.invokedExpression
if (invokedExpression.text == "apply") {
return callExpression
}
}
return null
}
return null;
}
private static boolean addExpressionInBlockIfNeeded(@NotNull String text, @NotNull GrClosableBlock block, boolean isFirst) {
if (block.getText().contains(text)) return false;
GrExpression newStatement = GroovyPsiElementFactory.getInstance(block.getProject()).createExpressionFromText(text);
CodeStyleManager.getInstance(block.getProject()).reformat(newStatement);
GrStatement[] statements = block.getStatements();
if (!isFirst && statements.length > 0) {
GrStatement lastStatement = statements[statements.length - 1];
if (lastStatement != null) {
block.addAfter(newStatement, lastStatement);
}
private fun showErrorMessage(project: Project, message: String?) {
Messages.showErrorDialog(project,
"<html>Couldn't configure kotlin-gradle plugin automatically.<br/>" +
(if (message != null) message + "<br/>" else "") +
"<br/>See manual installation instructions <a href=\"https://kotlinlang.org/docs/reference/using-gradle.html\">here</a>.</html>",
"Configure Kotlin-Gradle Plugin")
}
else {
PsiElement firstChild = block.getFirstChild();
if (firstChild != null) {
block.addAfter(newStatement, firstChild);
}
}
return true;
}
@Nullable
private static GrApplicationStatement getApplyStatement(@NotNull GroovyFile file) {
GrApplicationStatement[] applyStatement = PsiTreeUtil.getChildrenOfType(file, GrApplicationStatement.class);
if (applyStatement == null) return null;
for (GrApplicationStatement callExpression : applyStatement) {
GrExpression invokedExpression = callExpression.getInvokedExpression();
if (invokedExpression.getText().equals("apply")) {
return callExpression;
}
}
return null;
}
private static void showErrorMessage(@NotNull Project project, @Nullable String message) {
Messages.showErrorDialog(project,
"<html>Couldn't configure kotlin-gradle plugin automatically.<br/>" +
(message != null ? (message + "<br/>") : "") +
"<br/>See manual installation instructions <a href=\"https://kotlinlang.org/docs/reference/using-gradle.html\">here</a>.</html>",
"Configure Kotlin-Gradle Plugin");
}
}