Quick-fix for header without implementation + a set of tests #KT-14908 Fixed
This commit is contained in:
@@ -176,6 +176,10 @@ class KtPsiFactory(private val project: Project) {
|
|||||||
return createDeclaration(text)
|
return createDeclaration(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createObject(text: String): KtObjectDeclaration {
|
||||||
|
return createDeclaration(text)
|
||||||
|
}
|
||||||
|
|
||||||
fun createCompanionObject(): KtObjectDeclaration {
|
fun createCompanionObject(): KtObjectDeclaration {
|
||||||
return createClass("class A {\n companion object{\n}\n}").getCompanionObjects().first()
|
return createClass("class A {\n companion object{\n}\n}").getCompanionObjects().first()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,9 @@ private val MODIFIERS_TO_REPLACE = mapOf(
|
|||||||
PUBLIC_KEYWORD to listOf(PROTECTED_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD),
|
PUBLIC_KEYWORD to listOf(PROTECTED_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD),
|
||||||
PROTECTED_KEYWORD to listOf(PUBLIC_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD),
|
PROTECTED_KEYWORD to listOf(PUBLIC_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD),
|
||||||
PRIVATE_KEYWORD to listOf(PUBLIC_KEYWORD, PROTECTED_KEYWORD, INTERNAL_KEYWORD),
|
PRIVATE_KEYWORD to listOf(PUBLIC_KEYWORD, PROTECTED_KEYWORD, INTERNAL_KEYWORD),
|
||||||
INTERNAL_KEYWORD to listOf(PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD)
|
INTERNAL_KEYWORD to listOf(PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD),
|
||||||
|
HEADER_KEYWORD to listOf(IMPL_KEYWORD),
|
||||||
|
IMPL_KEYWORD to listOf(HEADER_KEYWORD)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val MODIFIERS_ORDER = listOf(PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD,
|
private val MODIFIERS_ORDER = listOf(PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createTypeParameter.Cr
|
|||||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateLocalVariableActionFactory
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateLocalVariableActionFactory
|
||||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterByNamedArgumentActionFactory
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterByNamedArgumentActionFactory
|
||||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterByRefActionFactory
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createVariable.CreateParameterByRefActionFactory
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.createImpl.CreateHeaderImplementationFix
|
||||||
import org.jetbrains.kotlin.idea.quickfix.migration.MigrateExternalExtensionFix
|
import org.jetbrains.kotlin.idea.quickfix.migration.MigrateExternalExtensionFix
|
||||||
import org.jetbrains.kotlin.idea.quickfix.migration.MigrateTypeParameterListFix
|
import org.jetbrains.kotlin.idea.quickfix.migration.MigrateTypeParameterListFix
|
||||||
import org.jetbrains.kotlin.idea.quickfix.replaceWith.DeprecatedSymbolUsageFix
|
import org.jetbrains.kotlin.idea.quickfix.replaceWith.DeprecatedSymbolUsageFix
|
||||||
@@ -461,6 +462,8 @@ class QuickFixRegistrar : QuickFixContributor {
|
|||||||
OVERLOADS_LOCAL.registerFactory(RemoveAnnotationFix.JvmOverloads)
|
OVERLOADS_LOCAL.registerFactory(RemoveAnnotationFix.JvmOverloads)
|
||||||
OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS.registerFactory(RemoveAnnotationFix.JvmOverloads)
|
OVERLOADS_WITHOUT_DEFAULT_ARGUMENTS.registerFactory(RemoveAnnotationFix.JvmOverloads)
|
||||||
|
|
||||||
|
HEADER_WITHOUT_IMPLEMENTATION.registerFactory(CreateHeaderImplementationFix)
|
||||||
|
|
||||||
ErrorsJs.WRONG_EXTERNAL_DECLARATION.registerFactory(MigrateExternalExtensionFix)
|
ErrorsJs.WRONG_EXTERNAL_DECLARATION.registerFactory(MigrateExternalExtensionFix)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+241
@@ -0,0 +1,241 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.quickfix.createImpl
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
|
import com.intellij.ide.util.PackageUtil
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.module.Module
|
||||||
|
import com.intellij.openapi.module.ModuleUtilCore
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.openapi.roots.ModuleRootManager
|
||||||
|
import com.intellij.psi.JavaDirectoryService
|
||||||
|
import com.intellij.psi.codeStyle.CodeStyleManager
|
||||||
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
|
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
|
||||||
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
|
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||||
|
import org.jetbrains.kotlin.idea.core.TemplateKind
|
||||||
|
import org.jetbrains.kotlin.idea.core.getFunctionBodyTextFromTemplate
|
||||||
|
import org.jetbrains.kotlin.idea.core.toDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.KotlinQuickFixAction
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.KotlinSingleIntentionActionFactory
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.getOrCreateKotlinFile
|
||||||
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||||
|
import org.jetbrains.kotlin.idea.util.projectStructure.allModules
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
|
||||||
|
sealed class CreateHeaderImplementationFix<out D : KtNamedDeclaration>(
|
||||||
|
declaration: D,
|
||||||
|
private val implPlatformKind: PlatformKind,
|
||||||
|
private val generateIt: KtPsiFactory.(Project, D) -> D?
|
||||||
|
) : KotlinQuickFixAction<D>(declaration) {
|
||||||
|
|
||||||
|
override fun getFamilyName() = text
|
||||||
|
|
||||||
|
protected abstract val elementType: String
|
||||||
|
|
||||||
|
override fun getText() = "Create header $elementType implementation for platform ${implPlatformKind.name}"
|
||||||
|
|
||||||
|
override final fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||||
|
val element = element ?: return
|
||||||
|
val factory = KtPsiFactory(project)
|
||||||
|
val implFile = getOrCreateImplementationFile(project) ?: return
|
||||||
|
|
||||||
|
val implDeclaration = implFile.add(factory.generateIt(project, element) ?: return) as KtElement
|
||||||
|
val reformatted = CodeStyleManager.getInstance(project).reformat(implDeclaration)
|
||||||
|
ShortenReferences.DEFAULT.process(reformatted as KtElement)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Project.implementationModuleOf(headerModule: Module) =
|
||||||
|
allModules().firstOrNull {
|
||||||
|
TargetPlatformDetector.getPlatform(it).kind == implPlatformKind &&
|
||||||
|
headerModule in ModuleRootManager.getInstance(it).dependencies
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getOrCreateImplementationFile(
|
||||||
|
project: Project
|
||||||
|
): KtFile? {
|
||||||
|
val declaration = element as? KtNamedDeclaration ?: return null
|
||||||
|
val name = declaration.name ?: return null
|
||||||
|
|
||||||
|
val headerDir = declaration.containingFile.containingDirectory
|
||||||
|
val headerPackage = JavaDirectoryService.getInstance().getPackage(headerDir)
|
||||||
|
|
||||||
|
val headerModule = ModuleUtilCore.findModuleForPsiElement(declaration) ?: return null
|
||||||
|
val implModule = project.implementationModuleOf(headerModule) ?: return null
|
||||||
|
val implDirectory = PackageUtil.findOrCreateDirectoryForPackage(
|
||||||
|
implModule, headerPackage?.qualifiedName ?: "", null, false
|
||||||
|
) ?: return null
|
||||||
|
return getOrCreateKotlinFile("$name.kt", implDirectory)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object : KotlinSingleIntentionActionFactory() {
|
||||||
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
|
val d = DiagnosticFactory.cast(diagnostic, Errors.HEADER_WITHOUT_IMPLEMENTATION)
|
||||||
|
val declaration = d.psiElement as? KtNamedDeclaration ?: return null
|
||||||
|
val compatibility = d.c
|
||||||
|
if (compatibility.isNotEmpty()) return null
|
||||||
|
val implPlatformKind = d.b.platformKind
|
||||||
|
return when (declaration) {
|
||||||
|
is KtClassOrObject -> CreateHeaderClassImplementationFix(declaration, implPlatformKind)
|
||||||
|
is KtFunction -> CreateHeaderFunctionImplementationFix(declaration, implPlatformKind)
|
||||||
|
is KtProperty -> CreateHeaderPropertyImplementationFix(declaration, implPlatformKind)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreateHeaderClassImplementationFix(
|
||||||
|
klass: KtClassOrObject,
|
||||||
|
implPlatformKind: PlatformKind
|
||||||
|
) : CreateHeaderImplementationFix<KtClassOrObject>(klass, implPlatformKind, { project, element ->
|
||||||
|
generateClassOrObject(project, element, implNeeded = true)
|
||||||
|
}) {
|
||||||
|
|
||||||
|
override val elementType = if ((element as? KtClass)?.isInterface() ?: false) "interface" else "class"
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreateHeaderPropertyImplementationFix(
|
||||||
|
property: KtProperty,
|
||||||
|
implPlatformKind: PlatformKind
|
||||||
|
) : CreateHeaderImplementationFix<KtProperty>(property, implPlatformKind, { project, element ->
|
||||||
|
val descriptor = element.toDescriptor() as? PropertyDescriptor
|
||||||
|
descriptor?.let { generateProperty(project, element, descriptor, implNeeded = true) }
|
||||||
|
}) {
|
||||||
|
|
||||||
|
override val elementType = "property"
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreateHeaderFunctionImplementationFix(
|
||||||
|
function: KtFunction,
|
||||||
|
implPlatformKind: PlatformKind
|
||||||
|
) : CreateHeaderImplementationFix<KtFunction>(function, implPlatformKind, { project, element ->
|
||||||
|
val descriptor = element.toDescriptor() as? FunctionDescriptor
|
||||||
|
descriptor?.let { generateFunction(project, element, descriptor, implNeeded = true) }
|
||||||
|
}) {
|
||||||
|
|
||||||
|
override val elementType = "function"
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtModifierListOwner.replaceHeaderModifier(implNeeded: Boolean) {
|
||||||
|
if (implNeeded) {
|
||||||
|
addModifier(KtTokens.IMPL_KEYWORD)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
removeModifier(KtTokens.HEADER_KEYWORD)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtPsiFactory.generateClassOrObject(
|
||||||
|
project: Project,
|
||||||
|
headerClass: KtClassOrObject,
|
||||||
|
implNeeded: Boolean
|
||||||
|
): KtClassOrObject {
|
||||||
|
val header = headerClass.text
|
||||||
|
val implClass = if (headerClass is KtObjectDeclaration) createObject(header) else createClass(header)
|
||||||
|
if (headerClass !is KtClass || !headerClass.isInterface()) {
|
||||||
|
implClass.declarations.forEach {
|
||||||
|
if (it !is KtEnumEntry &&
|
||||||
|
it !is KtClassOrObject &&
|
||||||
|
!it.hasModifier(KtTokens.ABSTRACT_KEYWORD)) {
|
||||||
|
it.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declLoop@ for (headerDeclaration in headerClass.declarations) {
|
||||||
|
if (headerDeclaration.hasModifier(KtTokens.ABSTRACT_KEYWORD)) continue
|
||||||
|
val descriptor = headerDeclaration.toDescriptor() ?: continue
|
||||||
|
val implDeclaration: KtDeclaration = when (headerDeclaration) {
|
||||||
|
is KtFunction -> generateFunction(project, headerDeclaration, descriptor as FunctionDescriptor, implNeeded = true)
|
||||||
|
is KtProperty -> generateProperty(project, headerDeclaration, descriptor as PropertyDescriptor, implNeeded = true)
|
||||||
|
else -> continue@declLoop
|
||||||
|
}
|
||||||
|
implClass.addDeclaration(implDeclaration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return implClass.apply {
|
||||||
|
replaceHeaderModifier(implNeeded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtPsiFactory.generateFunction(
|
||||||
|
project: Project,
|
||||||
|
headerFunction: KtFunction,
|
||||||
|
descriptor: FunctionDescriptor,
|
||||||
|
implNeeded: Boolean
|
||||||
|
): KtFunction {
|
||||||
|
val returnType = descriptor.returnType
|
||||||
|
val body = run {
|
||||||
|
if (returnType != null && !KotlinBuiltIns.isUnit(returnType)) {
|
||||||
|
val delegation = getFunctionBodyTextFromTemplate(
|
||||||
|
project,
|
||||||
|
TemplateKind.FUNCTION,
|
||||||
|
descriptor.name.asString(),
|
||||||
|
IdeDescriptorRenderers.SOURCE_CODE.renderType(returnType)
|
||||||
|
)
|
||||||
|
|
||||||
|
"{$delegation\n}"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
"{}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return if (headerFunction is KtSecondaryConstructor) {
|
||||||
|
createSecondaryConstructor(headerFunction.text + " " + body)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
createFunction(headerFunction.text + " " + body).apply {
|
||||||
|
replaceHeaderModifier(implNeeded)
|
||||||
|
if (returnType != null && KotlinBuiltIns.isUnit(returnType)) {
|
||||||
|
typeReference = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun KtPsiFactory.generateProperty(
|
||||||
|
project: Project,
|
||||||
|
headerProperty: KtProperty,
|
||||||
|
descriptor: PropertyDescriptor,
|
||||||
|
implNeeded: Boolean
|
||||||
|
): KtProperty {
|
||||||
|
val body = buildString {
|
||||||
|
append("\nget()")
|
||||||
|
append(" = ")
|
||||||
|
append(getFunctionBodyTextFromTemplate(
|
||||||
|
project,
|
||||||
|
TemplateKind.FUNCTION,
|
||||||
|
descriptor.name.asString(),
|
||||||
|
descriptor.returnType?.let { IdeDescriptorRenderers.SOURCE_CODE.renderType(it) } ?: ""
|
||||||
|
))
|
||||||
|
if (descriptor.isVar) {
|
||||||
|
append("\nset(value) {}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return createProperty(headerProperty.text + " " + body).apply {
|
||||||
|
replaceHeaderModifier(implNeeded)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// "Create header class implementation for platform JS" "true"
|
||||||
|
|
||||||
|
header abstract class <caret>Abstract {
|
||||||
|
fun foo(param: String): Int
|
||||||
|
|
||||||
|
abstract fun String.bar(y: Double): Boolean
|
||||||
|
|
||||||
|
val isGood: Boolean
|
||||||
|
|
||||||
|
abstract var status: Int
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// "Create header class implementation for platform JS" "true"
|
||||||
|
|
||||||
|
header abstract class Abstract {
|
||||||
|
fun foo(param: String): Int
|
||||||
|
|
||||||
|
abstract fun String.bar(y: Double): Boolean
|
||||||
|
|
||||||
|
val isGood: Boolean
|
||||||
|
|
||||||
|
abstract var status: Int
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// Abstract: to be implemented
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// Abstract: to be implemented
|
||||||
|
impl abstract class Abstract {
|
||||||
|
|
||||||
|
abstract fun String.bar(y: Double): Boolean
|
||||||
|
|
||||||
|
abstract var status: Int
|
||||||
|
impl fun foo(param: String): Int {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
impl val isGood: Boolean
|
||||||
|
get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// "Create header class implementation for platform JVM" "true"
|
||||||
|
|
||||||
|
header class <caret>My {
|
||||||
|
fun foo(param: String): Int
|
||||||
|
|
||||||
|
fun String.bar(y: Double): Boolean
|
||||||
|
|
||||||
|
fun baz(): Unit
|
||||||
|
|
||||||
|
constructor(flag: Boolean)
|
||||||
|
|
||||||
|
val isGood: Boolean
|
||||||
|
|
||||||
|
var status: Int
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// "Create header class implementation for platform JVM" "true"
|
||||||
|
|
||||||
|
header class My {
|
||||||
|
fun foo(param: String): Int
|
||||||
|
|
||||||
|
fun String.bar(y: Double): Boolean
|
||||||
|
|
||||||
|
fun baz(): Unit
|
||||||
|
|
||||||
|
constructor(flag: Boolean)
|
||||||
|
|
||||||
|
val isGood: Boolean
|
||||||
|
|
||||||
|
var status: Int
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// My: to be implemented
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// My: to be implemented
|
||||||
|
impl class My {
|
||||||
|
impl fun foo(param: String): Int {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fun String.bar(y: Double): Boolean {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fun baz() {}
|
||||||
|
|
||||||
|
constructor(flag: Boolean) {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
impl val isGood: Boolean
|
||||||
|
get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
impl var status: Int
|
||||||
|
get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
set(value) {}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// "Create header class implementation for platform JS" "true"
|
||||||
|
|
||||||
|
header enum class <caret>MyEnum {
|
||||||
|
FIRST,
|
||||||
|
SECOND,
|
||||||
|
LAST;
|
||||||
|
|
||||||
|
val num: Int
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun byNum(num: Int): MyEnum = when (num) {
|
||||||
|
1 -> FIRST
|
||||||
|
2 -> SECOND
|
||||||
|
else -> LAST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// "Create header class implementation for platform JS" "true"
|
||||||
|
|
||||||
|
header enum class MyEnum {
|
||||||
|
FIRST,
|
||||||
|
SECOND,
|
||||||
|
LAST;
|
||||||
|
|
||||||
|
val num: Int
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun byNum(num: Int): MyEnum = when (num) {
|
||||||
|
1 -> FIRST
|
||||||
|
2 -> SECOND
|
||||||
|
else -> LAST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// MyEnum: to be implemented
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
// MyEnum: to be implemented
|
||||||
|
impl enum class MyEnum {
|
||||||
|
FIRST,
|
||||||
|
SECOND,
|
||||||
|
LAST;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun byNum(num: Int): MyEnum = when (num) {
|
||||||
|
1 -> FIRST
|
||||||
|
2 -> SECOND
|
||||||
|
else -> LAST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl val num: Int
|
||||||
|
get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// "Create header function implementation for platform JVM" "true"
|
||||||
|
|
||||||
|
header fun <caret>foo(arg: Int): String
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// "Create header function implementation for platform JVM" "true"
|
||||||
|
|
||||||
|
header fun foo(arg: Int): String
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// foo: to be implemented
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// foo: to be implemented
|
||||||
|
impl fun foo(arg: Int): String {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// "Create header interface implementation for platform JVM" "true"
|
||||||
|
|
||||||
|
header interface <caret>Interface {
|
||||||
|
fun foo(param: String): Int
|
||||||
|
|
||||||
|
fun String.bar(y: Double): Boolean
|
||||||
|
|
||||||
|
val isGood: Boolean
|
||||||
|
|
||||||
|
var status: Int
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
// "Create header interface implementation for platform JVM" "true"
|
||||||
|
|
||||||
|
header interface Interface {
|
||||||
|
fun foo(param: String): Int
|
||||||
|
|
||||||
|
fun String.bar(y: Double): Boolean
|
||||||
|
|
||||||
|
val isGood: Boolean
|
||||||
|
|
||||||
|
var status: Int
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// Interface: to be implemented
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// Interface: to be implemented
|
||||||
|
impl interface Interface {
|
||||||
|
fun foo(param: String): Int
|
||||||
|
|
||||||
|
fun String.bar(y: Double): Boolean
|
||||||
|
|
||||||
|
val isGood: Boolean
|
||||||
|
|
||||||
|
var status: Int
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// "Create header class implementation for platform JVM" "true"
|
||||||
|
|
||||||
|
header class <caret>WithNested {
|
||||||
|
fun foo(): Int
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
fun bar() = "Nested"
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class Inner {
|
||||||
|
fun baz() = "Inner"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
// "Create header class implementation for platform JVM" "true"
|
||||||
|
|
||||||
|
header class WithNested {
|
||||||
|
fun foo(): Int
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
fun bar() = "Nested"
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class Inner {
|
||||||
|
fun baz() = "Inner"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// WithNested: to be implemented
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// WithNested: to be implemented
|
||||||
|
impl class WithNested {
|
||||||
|
|
||||||
|
class Nested {
|
||||||
|
fun bar() = "Nested"
|
||||||
|
}
|
||||||
|
|
||||||
|
inner class Inner {
|
||||||
|
fun baz() = "Inner"
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fun foo(): Int {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Create header class implementation for platform JVM" "true"
|
||||||
|
|
||||||
|
header object <caret>Object {
|
||||||
|
fun foo(): String
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
// "Create header class implementation for platform JVM" "true"
|
||||||
|
|
||||||
|
header object Object {
|
||||||
|
fun foo(): String
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// Object: to be implemented
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Object: to be implemented
|
||||||
|
impl object Object {
|
||||||
|
impl fun foo(): String {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// "Create header property implementation for platform JVM" "true"
|
||||||
|
|
||||||
|
header var <caret>x: Int
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// "Create header property implementation for platform JVM" "true"
|
||||||
|
|
||||||
|
header var x: Int
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// x: to be implemented
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// x: to be implemented
|
||||||
|
impl var x: Int
|
||||||
|
get() = TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
set(value) {}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Create header class implementation for platform JS" "true"
|
||||||
|
|
||||||
|
header sealed class <caret>Sealed {
|
||||||
|
object Obj : Sealed()
|
||||||
|
|
||||||
|
class Klass(val x: Int) : Sealed()
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Create header class implementation for platform JS" "true"
|
||||||
|
|
||||||
|
header sealed class Sealed {
|
||||||
|
object Obj : Sealed()
|
||||||
|
|
||||||
|
class Klass(val x: Int) : Sealed()
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
// Sealed: to be implemented
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
// Sealed: to be implemented
|
||||||
|
impl sealed class Sealed {
|
||||||
|
object Obj : Sealed()
|
||||||
|
|
||||||
|
class Klass(val x: Int) : Sealed()
|
||||||
|
}
|
||||||
+1
-84
@@ -16,32 +16,14 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.caches.resolve
|
package org.jetbrains.kotlin.idea.caches.resolve
|
||||||
|
|
||||||
import com.intellij.openapi.application.WriteAction
|
|
||||||
import com.intellij.openapi.command.WriteCommandAction
|
|
||||||
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProviderImpl
|
|
||||||
import com.intellij.openapi.module.Module
|
|
||||||
import com.intellij.openapi.module.StdModuleTypes
|
|
||||||
import com.intellij.openapi.roots.DependencyScope
|
|
||||||
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
|
||||||
import com.intellij.openapi.vfs.LocalFileSystem
|
|
||||||
import com.intellij.testFramework.PsiTestUtil
|
|
||||||
import com.sampullara.cli.Argument
|
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
|
||||||
import org.jetbrains.kotlin.config.CompilerSettings
|
|
||||||
import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider
|
|
||||||
import org.jetbrains.kotlin.config.TargetPlatformKind
|
|
||||||
import org.jetbrains.kotlin.idea.facet.getOrCreateFacet
|
|
||||||
import org.jetbrains.kotlin.idea.project.PluginJetFilesProvider
|
import org.jetbrains.kotlin.idea.project.PluginJetFilesProvider
|
||||||
import org.jetbrains.kotlin.idea.stubs.AbstractMultiHighlightingTest
|
import org.jetbrains.kotlin.idea.stubs.AbstractMultiHighlightingTest
|
||||||
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
|
||||||
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
import org.junit.Assert
|
import org.junit.Assert
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
abstract class AbstractMultiModuleHighlightingTest : AbstractMultiHighlightingTest() {
|
abstract class AbstractMultiModuleHighlightingTest : AbstractMultiHighlightingTest() {
|
||||||
|
|
||||||
protected open val testPath = PluginTestCaseBase.getTestDataPathBase() + "/multiModuleHighlighting/"
|
override val testPath = PluginTestCaseBase.getTestDataPathBase() + "/multiModuleHighlighting/"
|
||||||
|
|
||||||
protected fun checkHighlightingInAllFiles() {
|
protected fun checkHighlightingInAllFiles() {
|
||||||
var atLeastOneFile = false
|
var atLeastOneFile = false
|
||||||
@@ -54,69 +36,4 @@ abstract class AbstractMultiModuleHighlightingTest : AbstractMultiHighlightingTe
|
|||||||
}
|
}
|
||||||
Assert.assertTrue(atLeastOneFile)
|
Assert.assertTrue(atLeastOneFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun module(name: String, hasTestRoot: Boolean = false, useFullJdk: Boolean = false): Module {
|
|
||||||
val srcDir = testPath + "${getTestName(true)}/$name"
|
|
||||||
val moduleWithSrcRootSet = createModuleFromTestData(srcDir, name, StdModuleTypes.JAVA, true)!!
|
|
||||||
if (hasTestRoot) {
|
|
||||||
setTestRoot(moduleWithSrcRootSet, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
val jdkToUse = if (useFullJdk) PluginTestCaseBase.fullJdk() else PluginTestCaseBase.mockJdk()
|
|
||||||
ConfigLibraryUtil.configureSdk(moduleWithSrcRootSet, jdkToUse)
|
|
||||||
|
|
||||||
return moduleWithSrcRootSet
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun setTestRoot(module: Module, name: String) {
|
|
||||||
val testDir = testPath + "${getTestName(true)}/${name}Test"
|
|
||||||
val testRootDirInTestData = File(testDir)
|
|
||||||
val testRootDir = createTempDirectory()!!
|
|
||||||
FileUtil.copyDir(testRootDirInTestData, testRootDir)
|
|
||||||
val testRoot = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(testRootDir)!!
|
|
||||||
object : WriteCommandAction.Simple<Unit>(project) {
|
|
||||||
override fun run() {
|
|
||||||
testRoot.refresh(false, true)
|
|
||||||
}
|
|
||||||
}.execute().throwException()
|
|
||||||
PsiTestUtil.addSourceRoot(module, testRoot, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun Module.addDependency(
|
|
||||||
other: Module,
|
|
||||||
dependencyScope: DependencyScope = DependencyScope.COMPILE,
|
|
||||||
exported: Boolean = false
|
|
||||||
) = ModuleRootModificationUtil.addDependency(this, other, dependencyScope, exported)
|
|
||||||
|
|
||||||
private fun Module.createFacet() {
|
|
||||||
val accessToken = WriteAction.start()
|
|
||||||
try {
|
|
||||||
val modelsProvider = IdeModifiableModelsProviderImpl(project)
|
|
||||||
getOrCreateFacet(modelsProvider)
|
|
||||||
modelsProvider.commit()
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
accessToken.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun Module.setPlatformKind(platformKind: TargetPlatformKind<*>) {
|
|
||||||
createFacet()
|
|
||||||
val facetSettings = KotlinFacetSettingsProvider.getInstance(project).getSettings(this)
|
|
||||||
val versionInfo = facetSettings.versionInfo
|
|
||||||
versionInfo.targetPlatformKind = platformKind
|
|
||||||
}
|
|
||||||
|
|
||||||
protected fun Module.enableMultiPlatform() {
|
|
||||||
createFacet()
|
|
||||||
val facetSettings = KotlinFacetSettingsProvider.getInstance(project).getSettings(this)
|
|
||||||
val compilerInfo = facetSettings.compilerInfo
|
|
||||||
val compilerSettings = CompilerSettings()
|
|
||||||
compilerSettings.additionalArguments += " -$multiPlatformArg"
|
|
||||||
compilerInfo.compilerSettings = compilerSettings
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val multiPlatformArg = CommonCompilerArguments::multiPlatform.annotations.filterIsInstance<Argument>().single().value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.quickfix
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixTestCase
|
||||||
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
|
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler
|
||||||
|
import com.intellij.openapi.command.CommandProcessor
|
||||||
|
import com.intellij.openapi.util.text.StringUtil
|
||||||
|
import com.intellij.util.ui.UIUtil
|
||||||
|
import junit.framework.ComparisonFailure
|
||||||
|
import junit.framework.TestCase
|
||||||
|
import org.jetbrains.kotlin.idea.inspections.findExistingEditor
|
||||||
|
import org.jetbrains.kotlin.idea.project.PluginJetFilesProvider
|
||||||
|
import org.jetbrains.kotlin.idea.stubs.AbstractMultiModuleTest
|
||||||
|
import org.jetbrains.kotlin.idea.test.DirectiveBasedActionUtils
|
||||||
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
|
import java.io.File
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
abstract class AbstractQuickFixMultiModuleTest : AbstractMultiModuleTest() {
|
||||||
|
|
||||||
|
override val testPath = PluginTestCaseBase.getTestDataPathBase() + "/multiModuleQuickFix/"
|
||||||
|
|
||||||
|
override fun getTestDataPath() = testPath
|
||||||
|
|
||||||
|
protected fun shouldBeAvailableAfterExecution() = false
|
||||||
|
|
||||||
|
private fun getActionsTexts(availableActions: List<IntentionAction>) = availableActions.map { it.text }
|
||||||
|
|
||||||
|
protected fun doQuickFixTest() {
|
||||||
|
val allFilesInProject = PluginJetFilesProvider.allFilesInProject(myProject!!)
|
||||||
|
val actionFile = allFilesInProject.single { file ->
|
||||||
|
file.text.contains("// \"")
|
||||||
|
}
|
||||||
|
|
||||||
|
configureByExistingFile(actionFile.virtualFile!!)
|
||||||
|
|
||||||
|
val actionFileText = actionFile.text
|
||||||
|
val actionFileName = actionFile.name
|
||||||
|
|
||||||
|
CommandProcessor.getInstance().executeCommand(project, {
|
||||||
|
try {
|
||||||
|
val psiFile = actionFile
|
||||||
|
|
||||||
|
val pair = LightQuickFixTestCase.parseActionHint(psiFile, actionFileText)
|
||||||
|
val text = pair.getFirst()
|
||||||
|
|
||||||
|
val actionShouldBeAvailable = pair.getSecond()
|
||||||
|
|
||||||
|
if (psiFile is KtFile) {
|
||||||
|
DirectiveBasedActionUtils.checkForUnexpectedErrors(psiFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
doAction(text, actionShouldBeAvailable, actionFileName)
|
||||||
|
|
||||||
|
if (actionShouldBeAvailable) {
|
||||||
|
val testDirectory = File(testPath)
|
||||||
|
val projectDirectory = File("$testPath${getTestName(true)}")
|
||||||
|
for (moduleDirectory in projectDirectory.listFiles()) {
|
||||||
|
for (file in moduleDirectory.listFiles()) {
|
||||||
|
if (!file.path.endsWith(".after")) continue
|
||||||
|
try {
|
||||||
|
val editedFile = allFilesInProject.find {
|
||||||
|
it.name.toLowerCase() == file.name.removeSuffix(".after").toLowerCase()
|
||||||
|
}!!
|
||||||
|
setActiveEditor(editedFile.findExistingEditor() ?: createEditor(editedFile.virtualFile))
|
||||||
|
checkResultByFile(file.relativeTo(testDirectory).path)
|
||||||
|
}
|
||||||
|
catch (e: ComparisonFailure) {
|
||||||
|
KotlinTestUtils.assertEqualsToFile(file, editor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: ComparisonFailure) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
catch (e: AssertionError) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
catch (e: Throwable) {
|
||||||
|
e.printStackTrace()
|
||||||
|
TestCase.fail(getTestName(true))
|
||||||
|
}
|
||||||
|
}, "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: merge with AbstractQuickFixMultiFileTest
|
||||||
|
|
||||||
|
fun doAction(text: String, actionShouldBeAvailable: Boolean, testFilePath: String) {
|
||||||
|
val pattern = if (text.startsWith("/"))
|
||||||
|
Pattern.compile(text.substring(1, text.length - 1))
|
||||||
|
else
|
||||||
|
Pattern.compile(StringUtil.escapeToRegexp(text))
|
||||||
|
|
||||||
|
val availableActions = getAvailableActions()
|
||||||
|
val action = findActionByPattern(pattern, availableActions)
|
||||||
|
|
||||||
|
if (action == null) {
|
||||||
|
if (actionShouldBeAvailable) {
|
||||||
|
val texts = getActionsTexts(availableActions)
|
||||||
|
val infos = doHighlighting()
|
||||||
|
TestCase.fail("Action with text '" + text + "' is not available in test " + testFilePath + "\n" +
|
||||||
|
"Available actions (" + texts.size + "): \n" +
|
||||||
|
StringUtil.join(texts, "\n") +
|
||||||
|
"\nActions:\n" +
|
||||||
|
StringUtil.join(availableActions, "\n") +
|
||||||
|
"\nInfos:\n" +
|
||||||
|
StringUtil.join(infos, "\n"))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
DirectiveBasedActionUtils.checkAvailableActionsAreExpected(file, availableActions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!actionShouldBeAvailable) {
|
||||||
|
TestCase.fail("Action '$text' is available (but must not) in test $testFilePath")
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowIntentionActionsHandler.chooseActionAndInvoke(file, editor, action, action.text)
|
||||||
|
|
||||||
|
UIUtil.dispatchAllInvocationEvents()
|
||||||
|
|
||||||
|
|
||||||
|
if (!shouldBeAvailableAfterExecution()) {
|
||||||
|
val afterAction = findActionByPattern(pattern, getAvailableActions())
|
||||||
|
|
||||||
|
if (afterAction != null) {
|
||||||
|
TestCase.fail("Action '$text' is still available after its invocation in test $testFilePath")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun findActionByPattern(pattern: Pattern, availableActions: List<IntentionAction>): IntentionAction? {
|
||||||
|
for (availableAction in availableActions) {
|
||||||
|
if (pattern.matcher(availableAction.text).matches()) {
|
||||||
|
return availableAction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAvailableActions(): List<IntentionAction> {
|
||||||
|
doHighlighting()
|
||||||
|
return LightQuickFixTestCase.getAvailableActions(editor, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTestProjectJdk() = PluginTestCaseBase.mockJdk()
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.quickfix
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.config.TargetPlatformKind
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class QuickFixMultiModuleTest : AbstractQuickFixMultiModuleTest() {
|
||||||
|
|
||||||
|
private fun doMultiPlatformTest(headerName: String = "header",
|
||||||
|
implName: String = "jvm",
|
||||||
|
implKind: TargetPlatformKind<*> = TargetPlatformKind.Jvm.JVM_1_6) {
|
||||||
|
val header = module(headerName)
|
||||||
|
header.setPlatformKind(TargetPlatformKind.Common)
|
||||||
|
|
||||||
|
val jvm = module(implName)
|
||||||
|
jvm.setPlatformKind(implKind)
|
||||||
|
jvm.enableMultiPlatform()
|
||||||
|
jvm.addDependency(header)
|
||||||
|
|
||||||
|
doQuickFixTest()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAbstract() {
|
||||||
|
doMultiPlatformTest(implName = "js", implKind = TargetPlatformKind.JavaScript)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testClass() {
|
||||||
|
doMultiPlatformTest()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEnum() {
|
||||||
|
doMultiPlatformTest(implName = "js", implKind = TargetPlatformKind.JavaScript)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFunction() {
|
||||||
|
doMultiPlatformTest()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testInterface() {
|
||||||
|
doMultiPlatformTest()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testObject() {
|
||||||
|
doMultiPlatformTest()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testNested() {
|
||||||
|
doMultiPlatformTest()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testProperty() {
|
||||||
|
doMultiPlatformTest()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSealed() {
|
||||||
|
doMultiPlatformTest(implName = "js", implKind = TargetPlatformKind.JavaScript)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.idea.stubs
|
package org.jetbrains.kotlin.idea.stubs
|
||||||
|
|
||||||
import com.intellij.codeInsight.completion.CompletionTestCase
|
import com.intellij.codeInsight.completion.CompletionTestCase
|
||||||
|
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase
|
||||||
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
|
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl
|
||||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
@@ -29,7 +30,7 @@ import com.intellij.psi.search.GlobalSearchScope
|
|||||||
import com.intellij.psi.search.UsageSearchContext
|
import com.intellij.psi.search.UsageSearchContext
|
||||||
import com.intellij.testFramework.ExpectedHighlightingData
|
import com.intellij.testFramework.ExpectedHighlightingData
|
||||||
|
|
||||||
abstract class AbstractMultiHighlightingTest : CompletionTestCase() {
|
abstract class AbstractMultiHighlightingTest : AbstractMultiModuleTest() {
|
||||||
|
|
||||||
protected open val shouldCheckLineMarkers = false
|
protected open val shouldCheckLineMarkers = false
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2016 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.stubs
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase
|
||||||
|
import com.intellij.openapi.application.WriteAction
|
||||||
|
import com.intellij.openapi.command.WriteCommandAction
|
||||||
|
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProviderImpl
|
||||||
|
import com.intellij.openapi.module.Module
|
||||||
|
import com.intellij.openapi.module.StdModuleTypes
|
||||||
|
import com.intellij.openapi.roots.DependencyScope
|
||||||
|
import com.intellij.openapi.roots.ModuleRootModificationUtil
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.openapi.vfs.LocalFileSystem
|
||||||
|
import com.intellij.testFramework.PsiTestUtil
|
||||||
|
import com.sampullara.cli.Argument
|
||||||
|
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
|
||||||
|
import org.jetbrains.kotlin.config.CompilerSettings
|
||||||
|
import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider
|
||||||
|
import org.jetbrains.kotlin.config.TargetPlatformKind
|
||||||
|
import org.jetbrains.kotlin.idea.facet.getOrCreateFacet
|
||||||
|
import org.jetbrains.kotlin.idea.test.ConfigLibraryUtil
|
||||||
|
import org.jetbrains.kotlin.idea.test.PluginTestCaseBase
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class AbstractMultiModuleTest : DaemonAnalyzerTestCase() {
|
||||||
|
|
||||||
|
protected open val testPath = PluginTestCaseBase.getTestDataPathBase()
|
||||||
|
|
||||||
|
protected fun module(name: String, hasTestRoot: Boolean = false, useFullJdk: Boolean = false): Module {
|
||||||
|
val srcDir = testPath + "${getTestName(true)}/$name"
|
||||||
|
val moduleWithSrcRootSet = createModuleFromTestData(srcDir, name, StdModuleTypes.JAVA, true)!!
|
||||||
|
if (hasTestRoot) {
|
||||||
|
setTestRoot(moduleWithSrcRootSet, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val jdkToUse = if (useFullJdk) PluginTestCaseBase.fullJdk() else PluginTestCaseBase.mockJdk()
|
||||||
|
ConfigLibraryUtil.configureSdk(moduleWithSrcRootSet, jdkToUse)
|
||||||
|
|
||||||
|
return moduleWithSrcRootSet
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setTestRoot(module: Module, name: String) {
|
||||||
|
val testDir = testPath + "${getTestName(true)}/${name}Test"
|
||||||
|
val testRootDirInTestData = File(testDir)
|
||||||
|
val testRootDir = createTempDirectory()!!
|
||||||
|
FileUtil.copyDir(testRootDirInTestData, testRootDir)
|
||||||
|
val testRoot = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(testRootDir)!!
|
||||||
|
object : WriteCommandAction.Simple<Unit>(project) {
|
||||||
|
override fun run() {
|
||||||
|
testRoot.refresh(false, true)
|
||||||
|
}
|
||||||
|
}.execute().throwException()
|
||||||
|
PsiTestUtil.addSourceRoot(module, testRoot, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun Module.addDependency(
|
||||||
|
other: Module,
|
||||||
|
dependencyScope: DependencyScope = DependencyScope.COMPILE,
|
||||||
|
exported: Boolean = false
|
||||||
|
) = ModuleRootModificationUtil.addDependency(this, other, dependencyScope, exported)
|
||||||
|
|
||||||
|
private fun Module.createFacet() {
|
||||||
|
val accessToken = WriteAction.start()
|
||||||
|
try {
|
||||||
|
val modelsProvider = IdeModifiableModelsProviderImpl(project)
|
||||||
|
getOrCreateFacet(modelsProvider)
|
||||||
|
modelsProvider.commit()
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
accessToken.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun Module.setPlatformKind(platformKind: TargetPlatformKind<*>) {
|
||||||
|
createFacet()
|
||||||
|
val facetSettings = KotlinFacetSettingsProvider.getInstance(project).getSettings(this)
|
||||||
|
val versionInfo = facetSettings.versionInfo
|
||||||
|
versionInfo.targetPlatformKind = platformKind
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun Module.enableMultiPlatform() {
|
||||||
|
createFacet()
|
||||||
|
val facetSettings = KotlinFacetSettingsProvider.getInstance(project).getSettings(this)
|
||||||
|
val compilerInfo = facetSettings.compilerInfo
|
||||||
|
val compilerSettings = CompilerSettings()
|
||||||
|
compilerSettings.additionalArguments += " -$multiPlatformArg"
|
||||||
|
compilerInfo.compilerSettings = compilerSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val multiPlatformArg = CommonCompilerArguments::multiPlatform.annotations.filterIsInstance<Argument>().single().value
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user