Add kotlin.stdlib requirement to module-info on configure (KT-19207)
KotlinAddRequiredModuleFix is almost copy ofAddRequiredModuleFix from the platform but it moves actual fix method to static with less parameters and checks directive presence before add. #KT-19207 Fixed
This commit is contained in:
@@ -17,17 +17,21 @@
|
||||
package org.jetbrains.kotlin.idea.configuration
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.command.WriteCommandAction
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.JavaSdkVersion
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.*
|
||||
import com.intellij.openapi.roots.libraries.*
|
||||
import com.intellij.openapi.util.Computable
|
||||
import com.intellij.openapi.vfs.JarFileSystem
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VfsUtil
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import org.jetbrains.annotations.Contract
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider
|
||||
@@ -37,11 +41,16 @@ import org.jetbrains.kotlin.idea.KotlinPluginUtil
|
||||
import org.jetbrains.kotlin.idea.facet.getRuntimeLibraryVersion
|
||||
import org.jetbrains.kotlin.idea.framework.ui.CreateLibraryDialogWithModules
|
||||
import org.jetbrains.kotlin.idea.framework.ui.FileUIUtils
|
||||
import org.jetbrains.kotlin.idea.quickfix.KotlinAddRequiredModuleFix
|
||||
import org.jetbrains.kotlin.idea.quickfix.askUpdateRuntime
|
||||
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||
import org.jetbrains.kotlin.idea.util.findFirstPsiJavaModule
|
||||
import org.jetbrains.kotlin.idea.util.projectStructure.sdk
|
||||
import org.jetbrains.kotlin.idea.util.projectStructure.version
|
||||
import org.jetbrains.kotlin.idea.versions.LibraryJarDescriptor
|
||||
import org.jetbrains.kotlin.idea.versions.findAllUsedLibraries
|
||||
import org.jetbrains.kotlin.idea.versions.findKotlinRuntimeLibrary
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.KOTLIN_STDLIB_MODULE_NAME
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
|
||||
@@ -56,7 +65,7 @@ abstract class KotlinWithLibraryConfigurator internal constructor() : KotlinProj
|
||||
|
||||
open val libraryType: LibraryType<DummyLibraryProperties>? = null
|
||||
|
||||
protected val libraryKind: PersistentLibraryKind<*>? = libraryType?.kind
|
||||
protected val libraryKind: PersistentLibraryKind<*>? = libraryType?.kind
|
||||
|
||||
override fun getStatus(moduleSourceRootGroup: ModuleSourceRootGroup): ConfigureKotlinStatus {
|
||||
val module = moduleSourceRootGroup.baseModule
|
||||
@@ -129,12 +138,12 @@ abstract class KotlinWithLibraryConfigurator internal constructor() : KotlinProj
|
||||
pathFromDialog: String?,
|
||||
collector: NotificationMessageCollector
|
||||
) {
|
||||
val classesPath = getPathToCopyFileTo(module.project, OrderRootType.CLASSES, defaultPath, pathFromDialog)
|
||||
val sourcesPath = getPathToCopyFileTo(module.project, OrderRootType.SOURCES, defaultPath, pathFromDialog)
|
||||
val classesPath = getPathToCopyFileTo(module.project, OrderRootType.CLASSES, defaultPath, pathFromDialog)
|
||||
val sourcesPath = getPathToCopyFileTo(module.project, OrderRootType.SOURCES, defaultPath, pathFromDialog)
|
||||
configureModuleWithLibrary(module, classesPath, sourcesPath, collector, useBundled = pathFromDialog == null)
|
||||
}
|
||||
|
||||
fun configureModuleWithLibrary(
|
||||
fun configureModuleWithLibrary(
|
||||
module: Module,
|
||||
classesPath: String,
|
||||
sourcesPath: String,
|
||||
@@ -149,7 +158,7 @@ abstract class KotlinWithLibraryConfigurator internal constructor() : KotlinProj
|
||||
?: getKotlinLibrary(project)
|
||||
?: createNewLibrary(project, collector)
|
||||
|
||||
val sdk = ModuleRootManager.getInstance(module).sdk
|
||||
val sdk = module.sdk
|
||||
val model = library.modifiableModel
|
||||
|
||||
for (descriptor in getLibraryJarDescriptors(sdk)) {
|
||||
@@ -167,6 +176,23 @@ abstract class KotlinWithLibraryConfigurator internal constructor() : KotlinProj
|
||||
ApplicationManager.getApplication().runWriteAction { model.commit() }
|
||||
|
||||
addLibraryToModuleIfNeeded(module, library, collector)
|
||||
|
||||
updateModuleInfo(module, collector)
|
||||
}
|
||||
|
||||
private fun updateModuleInfo(module: Module, collector: NotificationMessageCollector) {
|
||||
if (module.sdk?.version?.isAtLeast(JavaSdkVersion.JDK_1_9) != true) return
|
||||
|
||||
val project = module.project
|
||||
val javaModule: PsiJavaModule = findFirstPsiJavaModule(module) ?: return
|
||||
|
||||
val success = WriteCommandAction.runWriteCommandAction(project, Computable<Boolean> {
|
||||
KotlinAddRequiredModuleFix.addModuleRequirement(javaModule, KOTLIN_STDLIB_MODULE_NAME)
|
||||
})
|
||||
|
||||
if (success) {
|
||||
collector.addMessage("Added $KOTLIN_STDLIB_MODULE_NAME requirement to module-info in ${module.name}")
|
||||
}
|
||||
}
|
||||
|
||||
fun configureLibraryJar(
|
||||
|
||||
@@ -16,15 +16,38 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.quickfix
|
||||
|
||||
import com.intellij.codeInsight.daemon.QuickFixBundle
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.JavaModuleGraphUtil
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.AddRequiredModuleFix
|
||||
import com.intellij.codeInsight.intention.IntentionAction
|
||||
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.util.PsiUtil
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||
import org.jetbrains.kotlin.idea.util.findRequireDirective
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||
|
||||
class KotlinAddRequiredModuleFix {
|
||||
class KotlinAddRequiredModuleFix(module: PsiJavaModule, private val requiredName: String) : LocalQuickFixAndIntentionActionOnPsiElement(module) {
|
||||
override fun getFamilyName(): String = QuickFixBundle.message("module.info.add.requires.family.name")
|
||||
override fun getText(): String = QuickFixBundle.message("module.info.add.requires.name", requiredName);
|
||||
override fun startInWriteAction() = true
|
||||
|
||||
override fun isAvailable(project: Project, file: PsiFile, startElement: PsiElement, endElement: PsiElement): Boolean {
|
||||
return PsiUtil.isLanguageLevel9OrHigher(file) &&
|
||||
startElement is PsiJavaModule &&
|
||||
startElement.getManager().isInProject(startElement) &&
|
||||
getLBrace(startElement) != null;
|
||||
}
|
||||
|
||||
override fun invoke(project: Project, file: PsiFile, editor: Editor?, startElement: PsiElement, endElement: PsiElement) {
|
||||
addModuleRequirement(startElement as PsiJavaModule, requiredName)
|
||||
}
|
||||
|
||||
companion object : KotlinSingleIntentionActionFactory() {
|
||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||
val expression = diagnostic.psiElement as? KtExpression ?: return null
|
||||
@@ -33,7 +56,38 @@ class KotlinAddRequiredModuleFix {
|
||||
val dependDiagnostic = DiagnosticFactory.cast(diagnostic, ErrorsJvm.JAVA_MODULE_DOES_NOT_DEPEND_ON_MODULE)
|
||||
val moduleName = dependDiagnostic.a
|
||||
|
||||
return AddRequiredModuleFix(javaModule, moduleName)
|
||||
return KotlinAddRequiredModuleFix(javaModule, moduleName)
|
||||
}
|
||||
|
||||
fun addModuleRequirement(module: PsiJavaModule, requiredName: String): Boolean {
|
||||
if (!module.isValid) return false
|
||||
if (findRequireDirective(module, requiredName) != null) return false
|
||||
|
||||
val parserFacade = JavaPsiFacade.getInstance(module.project).parserFacade
|
||||
val tempModule = parserFacade.createModuleFromText("module TempModuleName { requires $requiredName; }")
|
||||
val requiresStatement = tempModule.requires.first()
|
||||
|
||||
val addingPlace = findAddingPlace(module) ?: return false
|
||||
addingPlace.parent.addAfter(requiresStatement, addingPlace)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getLBrace(module: PsiJavaModule): PsiElement? {
|
||||
val nameElement = module.nameIdentifier
|
||||
var element: PsiElement? = nameElement.nextSibling
|
||||
while (element != null) {
|
||||
if (PsiUtil.isJavaToken(element, JavaTokenType.LBRACE)) {
|
||||
return element
|
||||
}
|
||||
element = element.nextSibling
|
||||
}
|
||||
return null // module-info is incomplete
|
||||
}
|
||||
|
||||
private fun findAddingPlace(module: PsiJavaModule): PsiElement? {
|
||||
val addingPlace = module.requires.lastOrNull()
|
||||
return addingPlace ?: getLBrace(module)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2010-2017 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.kotlin.idea.util
|
||||
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.psi.PsiJavaFile
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.PsiRequiresStatement
|
||||
import com.intellij.psi.search.FilenameIndex
|
||||
|
||||
fun findFirstPsiJavaModule(module: Module): PsiJavaModule? {
|
||||
val project = module.project
|
||||
|
||||
val moduleInfoFiles = FilenameIndex.getVirtualFilesByName(project, PsiJavaModule.MODULE_INFO_FILE, module.moduleScope)
|
||||
return moduleInfoFiles
|
||||
.asSequence()
|
||||
.map(PsiManager.getInstance(project)::findFile)
|
||||
.filterIsInstance<PsiJavaFile>()
|
||||
.map { it.moduleDeclaration }
|
||||
.firstOrNull { it != null }
|
||||
}
|
||||
|
||||
fun findRequireDirective(module: PsiJavaModule, requiredName: String): PsiRequiresStatement? =
|
||||
module.requires.find { it.moduleName == requiredName }
|
||||
Reference in New Issue
Block a user