diff --git a/java/java-impl/src/com/intellij/refactoring/util/RefactoringMessageUtil.java b/java/java-impl/src/com/intellij/refactoring/util/RefactoringMessageUtil.java index f3b169823f49..d0db9f5fd1e4 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/RefactoringMessageUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/util/RefactoringMessageUtil.java @@ -2,8 +2,10 @@ // Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.refactoring.util; +import com.intellij.ide.highlighter.JavaFileType; import com.intellij.java.refactoring.JavaRefactoringBundle; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.NlsContexts; import com.intellij.openapi.vfs.VirtualFile; @@ -30,6 +32,10 @@ public final class RefactoringMessageUtil { * an error message, if cannot create a class */ public static @Nls(capitalization = Sentence) @Nullable String checkCanCreateClass(PsiDirectory destinationDirectory, String className) { + return checkCanCreateClass(destinationDirectory, className, JavaFileType.INSTANCE); + } + + public static @Nls(capitalization = Sentence) @Nullable String checkCanCreateClass(PsiDirectory destinationDirectory, String className, FileType fileType) { PsiClass[] classes = JavaDirectoryService.getInstance().getClasses(destinationDirectory); VirtualFile file = destinationDirectory.getVirtualFile(); for (PsiClass aClass : classes) { @@ -38,7 +44,7 @@ public final class RefactoringMessageUtil { file.getPresentableUrl(), UsageViewUtil.getType(aClass), className); } } - @NonNls String fileName = className + ".java"; + @NonNls String fileName = className + "." + fileType.getDefaultExtension(); return checkCanCreateFile(destinationDirectory, fileName); } diff --git a/java/java-impl/src/com/intellij/testIntegration/TestIntegrationUtils.java b/java/java-impl/src/com/intellij/testIntegration/TestIntegrationUtils.java index 683eef8d353e..251dc226319f 100644 --- a/java/java-impl/src/com/intellij/testIntegration/TestIntegrationUtils.java +++ b/java/java-impl/src/com/intellij/testIntegration/TestIntegrationUtils.java @@ -176,8 +176,30 @@ public final class TestIntegrationUtils { final PsiClass targetClass, final PsiMethod method, boolean automatic, final Template template) { + runTestMethodTemplate(editor, targetClass, method, method.getModifierList(), automatic, template); + } - final int startOffset = method.getModifierList().getTextRange().getStartOffset(); + public static void runTestMethodTemplate(@NotNull MethodKind methodKind, + TestFramework framework, + final Editor editor, + final PsiElement targetClass, + @Nullable PsiClass sourceClass, + final PsiElement method, + final PsiElement methodModifierList, + @Nullable String name, + boolean automatic, + Set existingNames) { + runTestMethodTemplate(editor, targetClass, method, methodModifierList, automatic, + createTestMethodTemplate(methodKind, framework, targetClass, sourceClass, name, automatic, existingNames)); + } + + public static void runTestMethodTemplate(final Editor editor, + final PsiElement targetClass, + final PsiElement method, + final PsiElement methodModifierList, + boolean automatic, + final Template template) { + final int startOffset = methodModifierList.getTextRange().getStartOffset(); final TextRange range = new TextRange(startOffset, method.getTextRange().getEndOffset()); editor.getDocument().replaceString(range.getStartOffset(), range.getEndOffset(), ""); editor.getCaretModel().moveToOffset(range.getStartOffset()); @@ -228,14 +250,25 @@ public final class TestIntegrationUtils { @Nullable String name, boolean automatic, Set existingNames) { + return createTestMethodTemplate(methodKind, descriptor, (PsiElement) targetClass, sourceClass, name, automatic, existingNames); + } + + public static Template createTestMethodTemplate(@NotNull MethodKind methodKind, + TestFramework descriptor, + @NotNull PsiElement targetClass, + @Nullable PsiClass sourceClass, + @Nullable String name, + boolean automatic, + Set existingNames) { FileTemplateDescriptor templateDesc = methodKind.getFileTemplateDescriptor(descriptor); String templateName = templateDesc.getFileName(); - FileTemplate fileTemplate = FileTemplateManager.getInstance(targetClass.getProject()).getCodeTemplate(templateName); - Template template = TemplateManager.getInstance(targetClass.getProject()).createTemplate("", ""); + Project project = targetClass.getProject(); + FileTemplate fileTemplate = FileTemplateManager.getInstance(project).getCodeTemplate(templateName); + Template template = TemplateManager.getInstance(project).createTemplate("", ""); String templateText; try { - Properties properties = FileTemplateManager.getInstance(targetClass.getProject()).getDefaultProperties(); + Properties properties = FileTemplateManager.getInstance(project).getDefaultProperties(); if (sourceClass != null && sourceClass.isValid()) { properties.setProperty(FileTemplate.ATTRIBUTE_CLASS_NAME, sourceClass.getQualifiedName()); } diff --git a/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties b/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties index 755f3de622e3..9f95ab2909d9 100644 --- a/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties +++ b/plugins/kotlin/base/resources/resources-en/messages/KotlinBundle.properties @@ -307,6 +307,10 @@ intention.wrap.in.with.context=Wrap call in 'withContext' intention.flow.on.dispatchers.io=Flow on 'Dispatchers.IO' intention.switch.context.to.dispatchers.io=Switch to 'Dispatchers.IO' context +intention.error.cannot.create.class.message=Cannot Create Class ''{0}'' +intention.error.cannot.create.class.title=Failed to Create Class +intention.create.test.dialog.kotlin=Kotlin + # Code insight copy.paste.select.imports.to.remove.dialog=Select Imports to Remove copy.paste.select.imports.to.remove.text=The code fragment which you have pasted uses elements that are not accessible in the new context. Necessary imports have been added.
Select which of the added imports you want to remove from the file. @@ -2704,4 +2708,4 @@ progress.title.converting.to.if.then.else.expression=Converting to if-then-else progress.title.introducing.value.for.condition=Introducing value for condition\u2026 inspection.kotlin.options.to.compiler.options.display.name=Use of deprecated 'kotlinOptions' DSL -replace.kotlin.options.with.compiler.options=Replace 'kotlinOptions' with 'compilerOptions' +replace.kotlin.options.with.compiler.options=Replace 'kotlinOptions' with 'compilerOptions' \ No newline at end of file diff --git a/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/testIntegration/AbstractKotlinCreateTestIntention.kt b/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/testIntegration/AbstractKotlinCreateTestIntention.kt index e59934f29f66..c563770dc5c7 100644 --- a/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/testIntegration/AbstractKotlinCreateTestIntention.kt +++ b/plugins/kotlin/code-insight/impl-base/src/org/jetbrains/kotlin/idea/codeinsights/impl/base/testIntegration/AbstractKotlinCreateTestIntention.kt @@ -6,7 +6,6 @@ import com.intellij.CommonBundle import com.intellij.codeInsight.CodeInsightBundle import com.intellij.codeInsight.FileModificationService import com.intellij.ide.util.PropertiesComponent -import com.intellij.lang.java.JavaLanguage import com.intellij.model.SideEffectGuard import com.intellij.openapi.editor.Editor import com.intellij.openapi.module.Module @@ -15,7 +14,6 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.ui.Messages import com.intellij.openapi.util.TextRange -import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.* import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.GlobalSearchScopesCore @@ -47,11 +45,11 @@ abstract class AbstractKotlinCreateTestIntention : SelfTargetingRangeIntention( + KotlinBundle.message("convert.class.0.to.kotlin", generatedClass.name.toString()), + this + ) { + runWriteAction { + generatedClass.methods.forEach { + it.throwsList.referenceElements.forEach { referenceElement -> referenceElement.delete() } + } + } + + if (existingClass != null) { + activateFileWithPsiElement(existingClass) + } else { + with(PsiDocumentManager.getInstance(project)) { + getDocument(generatedFile)?.let { doPostponedOperationsAndUnblockDocument(it) } + } + } + } } } \ No newline at end of file diff --git a/plugins/kotlin/code-insight/kotlin.code-insight.k2/resources/kotlin.code-insight.k2.xml b/plugins/kotlin/code-insight/kotlin.code-insight.k2/resources/kotlin.code-insight.k2.xml index 24cd122cdc15..ac7d29536423 100644 --- a/plugins/kotlin/code-insight/kotlin.code-insight.k2/resources/kotlin.code-insight.k2.xml +++ b/plugins/kotlin/code-insight/kotlin.code-insight.k2/resources/kotlin.code-insight.k2.xml @@ -245,6 +245,8 @@ implementationClass="org.jetbrains.kotlin.idea.k2.codeinsight.hierarchy.overrides.KotlinOverrideHierarchyProvider"/> + + diff --git a/plugins/kotlin/code-insight/kotlin.code-insight.k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/generate/KotlinGenerateTestSupportActionBase.kt b/plugins/kotlin/code-insight/kotlin.code-insight.k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/generate/KotlinGenerateTestSupportActionBase.kt index e5f4845c9b9a..4a5acb7fcc1e 100644 --- a/plugins/kotlin/code-insight/kotlin.code-insight.k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/generate/KotlinGenerateTestSupportActionBase.kt +++ b/plugins/kotlin/code-insight/kotlin.code-insight.k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/generate/KotlinGenerateTestSupportActionBase.kt @@ -47,6 +47,105 @@ abstract class KotlinGenerateTestSupportActionBase( private val methodKind: MethodKind, ) : KotlinGenerateActionBase(), GenerateActionPopupTemplateInjector { companion object { + private val DUMMY_NAME = "__KOTLIN_RULEZZZ__" + + internal fun doGenerate( + editor: Editor, + file: PsiFile, klass: KtClassOrObject, + framework: TestFramework, + methodKind: MethodKind + ): KtNamedFunction? { + val project = file.project + val commandName = KotlinBundle.message("command.generate.test.support.generate.test.function") + + val fileTemplateDescriptor = methodKind.getFileTemplateDescriptor(framework) + val fileTemplate = FileTemplateManager.getInstance(project).getCodeTemplate(fileTemplateDescriptor.fileName) + var templateText = fileTemplate.text.replace(BODY_VAR, "") + var name: String? = null + if (templateText.contains(NAME_VAR)) { + name = if (templateText.contains("test$NAME_VAR")) "Name" else "name" + if (!isUnitTestMode()) { + val message = KotlinBundle.message("action.generate.test.support.choose.test.name") + name = Messages.showInputDialog(message, commandName, null, name, NAME_VALIDATOR) ?: return null + } + + templateText = templateText.replace(NAME_VAR, DUMMY_NAME) + } + + return try { + val factory = KtPsiFactory(project) + var function = factory.createFunction(templateText) + name?.let { + function = substituteNewName(function, it) + } + val functionInPlace = runWriteAction { insertMembersAfterAndReformat(editor, klass, function) } + + val (bodyText, needToOverride) = analyzeInModalWindow(functionInPlace, commandName) { + val functionSymbol = functionInPlace.symbol as KaNamedFunctionSymbol + val overriddenSymbols = functionSymbol.directlyOverriddenSymbols.filterIsInstance().toList() + + fun isDefaultTemplate(): Boolean = + (functionInPlace.bodyBlockExpression?.text?.trimStart('{')?.trimEnd('}') ?: functionInPlace.bodyExpression?.text).isNullOrBlank() + + when (overriddenSymbols.size) { + 0 -> if (isDefaultTemplate()) generateUnsupportedOrSuperCall(project, functionSymbol, BodyType.FromTemplate) else null + 1 -> generateUnsupportedOrSuperCall(project, overriddenSymbols.single(), BodyType.Super) + else -> generateUnsupportedOrSuperCall(project, overriddenSymbols.first(), BodyType.QualifiedSuper) + } to overriddenSymbols.isNotEmpty() + } + + runWriteAction { + if (bodyText != null) { + functionInPlace.bodyExpression?.delete() + functionInPlace.add(KtPsiFactory(project).createBlock(bodyText)) + } + + if (needToOverride) { + functionInPlace.addModifier(KtTokens.OVERRIDE_KEYWORD) + } + } + + setupEditorSelection(editor, functionInPlace) + functionInPlace + } catch (e: IncorrectOperationException) { + val message = KotlinBundle.message("action.generate.test.support.error.cant.generate.method", e.message.toString()) + HintManager.getInstance().showErrorHint(editor, message) + null + } + } + + private fun substituteNewName(function: KtNamedFunction, name: String): KtNamedFunction { + val psiFactory = KtPsiFactory(function.project) + + // First replace all DUMMY_NAME occurrences in names as they need special treatment due to quotation + var function1 = function + function1.accept( + object : KtTreeVisitorVoid() { + private fun getNewId(currentId: String): String? { + if (!currentId.contains(DUMMY_NAME)) return null + return currentId.replace(DUMMY_NAME, name).quoteIfNeeded() + } + + override fun visitNamedDeclaration(declaration: KtNamedDeclaration) { + val nameIdentifier = declaration.nameIdentifier ?: return + val newId = getNewId(nameIdentifier.text) ?: return + declaration.setName(newId) + } + + override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) { + val newId = getNewId(expression.text) ?: return + expression.replace(psiFactory.createSimpleName(newId)) + } + } + ) + // Then text-replace remaining occurrences (if any) + val functionText = function1.text + if (functionText.contains(DUMMY_NAME)) { + function1 = psiFactory.createFunction(function1.text.replace(DUMMY_NAME, name)) + } + return function1 + } + private fun findTargetClass(editor: Editor, file: PsiFile): KtClassOrObject? { val elementAtCaret = file.findElementAt(editor.caretModel.offset) ?: return null return elementAtCaret.parentsWithSelf.filterIsInstance().firstOrNull { !it.isLocal } @@ -85,7 +184,7 @@ abstract class KotlinGenerateTestSupportActionBase( } class Test : KotlinGenerateTestSupportActionBase(MethodKind.TEST) { - override fun isApplicableTo(framework: TestFramework, targetClass: KtClassOrObject) = true + override fun isApplicableTo(framework: TestFramework, targetClass: KtClassOrObject): Boolean = true } class Data : KotlinGenerateTestSupportActionBase(MethodKind.DATA) { @@ -117,7 +216,7 @@ abstract class KotlinGenerateTestSupportActionBase( if (testFrameworkToUse != null) { val frameworkToUse = findSuitableFrameworks(klass).first { it.name == testFrameworkToUse } if (isApplicableTo(frameworkToUse, klass)) { - doGenerate(editor, file, klass, frameworkToUse) + doGenerate(editor, file, klass, frameworkToUse, methodKind) } } else { val frameworks = findSuitableFrameworks(klass).filter { @@ -126,7 +225,7 @@ abstract class KotlinGenerateTestSupportActionBase( chooseAndPerform(editor, frameworks) { project.executeCommand(KotlinBundle.message("command.generate.test.support.generate.test.function"), null) { - doGenerate(editor, file, klass, it) + doGenerate(editor, file, klass, it, methodKind) } } } @@ -134,99 +233,6 @@ abstract class KotlinGenerateTestSupportActionBase( var testFrameworkToUse: String? = null - private val DUMMY_NAME = "__KOTLIN_RULEZZZ__" - - private fun doGenerate(editor: Editor, file: PsiFile, klass: KtClassOrObject, framework: TestFramework) { - val project = file.project - val commandName = KotlinBundle.message("command.generate.test.support.generate.test.function") - - val fileTemplateDescriptor = methodKind.getFileTemplateDescriptor(framework) - val fileTemplate = FileTemplateManager.getInstance(project).getCodeTemplate(fileTemplateDescriptor.fileName) - var templateText = fileTemplate.text.replace(BODY_VAR, "") - var name: String? = null - if (templateText.contains(NAME_VAR)) { - name = if (templateText.contains("test$NAME_VAR")) "Name" else "name" - if (!isUnitTestMode()) { - val message = KotlinBundle.message("action.generate.test.support.choose.test.name") - name = Messages.showInputDialog(message, commandName, null, name, NAME_VALIDATOR) ?: return - } - - templateText = templateText.replace(NAME_VAR, DUMMY_NAME) - } - - try { - val factory = KtPsiFactory(project) - var function = factory.createFunction(templateText) - name?.let { - function = substituteNewName(function, it) - } - val functionInPlace = runWriteAction { insertMembersAfterAndReformat(editor, klass, function) } - - val (bodyText, needToOverride) = analyzeInModalWindow(functionInPlace, commandName) { - val functionSymbol = functionInPlace.symbol as KaNamedFunctionSymbol - val overriddenSymbols = functionSymbol.directlyOverriddenSymbols.filterIsInstance().toList() - - fun isDefaultTemplate(): Boolean = - (functionInPlace.bodyBlockExpression?.text?.trimStart('{')?.trimEnd('}') ?: functionInPlace.bodyExpression?.text).isNullOrBlank() - - when (overriddenSymbols.size) { - 0 -> if (isDefaultTemplate()) generateUnsupportedOrSuperCall(project, functionSymbol, BodyType.FromTemplate) else null - 1 -> generateUnsupportedOrSuperCall(project, overriddenSymbols.single(), BodyType.Super) - else -> generateUnsupportedOrSuperCall(project, overriddenSymbols.first(), BodyType.QualifiedSuper) - } to overriddenSymbols.isNotEmpty() - } - - runWriteAction { - if (bodyText != null) { - functionInPlace.bodyExpression?.delete() - functionInPlace.add(KtPsiFactory(project).createBlock(bodyText)) - } - - if (needToOverride) { - functionInPlace.addModifier(KtTokens.OVERRIDE_KEYWORD) - } - } - - setupEditorSelection(editor, functionInPlace) - - } catch (e: IncorrectOperationException) { - val message = KotlinBundle.message("action.generate.test.support.error.cant.generate.method", e.message.toString()) - HintManager.getInstance().showErrorHint(editor, message) - } - } - - private fun substituteNewName(function: KtNamedFunction, name: String): KtNamedFunction { - val psiFactory = KtPsiFactory(function.project) - - // First replace all DUMMY_NAME occurrences in names as they need special treatment due to quotation - var function1 = function - function1.accept( - object : KtTreeVisitorVoid() { - private fun getNewId(currentId: String): String? { - if (!currentId.contains(DUMMY_NAME)) return null - return currentId.replace(DUMMY_NAME, name).quoteIfNeeded() - } - - override fun visitNamedDeclaration(declaration: KtNamedDeclaration) { - val nameIdentifier = declaration.nameIdentifier ?: return - val newId = getNewId(nameIdentifier.text) ?: return - declaration.setName(newId) - } - - override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) { - val newId = getNewId(expression.text) ?: return - expression.replace(psiFactory.createSimpleName(newId)) - } - } - ) - // Then text-replace remaining occurrences (if any) - val functionText = function1.text - if (functionText.contains(DUMMY_NAME)) { - function1 = psiFactory.createFunction(function1.text.replace(DUMMY_NAME, name)) - } - return function1 - } - override fun createEditTemplateAction(dataContext: DataContext): AnAction? { val project = CommonDataKeys.PROJECT.getData(dataContext) ?: return null val editor = CommonDataKeys.EDITOR.getData(dataContext) ?: return null diff --git a/plugins/kotlin/code-insight/kotlin.code-insight.k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/generate/KotlinTestGenerator.kt b/plugins/kotlin/code-insight/kotlin.code-insight.k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/generate/KotlinTestGenerator.kt new file mode 100644 index 000000000000..34944226b5d8 --- /dev/null +++ b/plugins/kotlin/code-insight/kotlin.code-insight.k2/src/org/jetbrains/kotlin/idea/k2/codeinsight/generate/KotlinTestGenerator.kt @@ -0,0 +1,249 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package org.jetbrains.kotlin.idea.k2.codeinsight.generate + +import com.intellij.codeInsight.CodeInsightUtil +import com.intellij.codeInsight.daemon.impl.analysis.ImportsHighlightUtil +import com.intellij.ide.fileTemplates.FileTemplate +import com.intellij.ide.fileTemplates.FileTemplateDescriptor +import com.intellij.ide.fileTemplates.FileTemplateManager +import com.intellij.ide.fileTemplates.FileTemplateUtil +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.Messages +import com.intellij.openapi.util.Comparing +import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.* +import com.intellij.psi.impl.source.PostprocessReformattingAspect +import com.intellij.psi.search.GlobalSearchScopesCore +import com.intellij.psi.util.PsiUtilCore +import com.intellij.refactoring.util.classMembers.MemberInfo +import com.intellij.testIntegration.TestFramework +import com.intellij.testIntegration.TestIntegrationUtils +import com.intellij.testIntegration.TestIntegrationUtils.MethodKind +import com.intellij.testIntegration.createTest.CreateTestDialog +import com.intellij.testIntegration.createTest.TestGenerator +import com.intellij.util.IncorrectOperationException +import org.jetbrains.kotlin.analysis.api.permissions.KaAllowAnalysisFromWriteAction +import org.jetbrains.kotlin.analysis.api.permissions.KaAllowAnalysisOnEdt +import org.jetbrains.kotlin.analysis.api.permissions.allowAnalysisFromWriteAction +import org.jetbrains.kotlin.analysis.api.permissions.allowAnalysisOnEdt +import org.jetbrains.kotlin.idea.KotlinFileType +import org.jetbrains.kotlin.idea.base.codeInsight.ShortenReferencesFacility +import org.jetbrains.kotlin.idea.base.resources.KotlinBundle +import org.jetbrains.kotlin.idea.core.insertMembersAfter +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.psi.* +import java.util.* + +class KotlinTestGenerator: TestGenerator { + override fun generateTest(project: Project, d: CreateTestDialog): PsiElement? { + return PostprocessReformattingAspect.getInstance(project).postponeFormattingInside { + runWriteAction { + try { + IdeDocumentHistory.getInstance(project).includeCurrentPlaceAsChangePlace() + + val targetClass = createTestClass(d) as? KtClass ?: return@runWriteAction null + val frameworkDescriptor = d.selectedTestFrameworkDescriptor + val defaultSuperClass = frameworkDescriptor.getDefaultSuperClass() + d.getSuperClassName().takeIf { !Comparing.strEqual(it, defaultSuperClass) }?.let { superClassName -> + addSuperClass(targetClass, project, superClassName) + } + + val file = targetClass.containingFile + val editor = CodeInsightUtil.positionCursor( + project, + file, + targetClass.body?.lBrace ?: targetClass + ) ?: return@runWriteAction null + + addTestMethods( + editor, + targetClass, + d.targetClass, + frameworkDescriptor, + d.selectedMethods, + d.shouldGeneratedBefore(), + d.shouldGeneratedAfter() + ) + + if (file is KtFile) { + val list = file.importList + if (list != null) { + val importStatements = file.importDirectives + if (importStatements.isNotEmpty()) { + val virtualFile = PsiUtilCore.getVirtualFile(list) + if (virtualFile != null) { + val imports: MutableSet = HashSet() + for (base in importStatements) { + imports.add(base.text) + } + virtualFile.putCopyableUserData?>( + ImportsHighlightUtil.IMPORTS_FROM_TEMPLATE, + imports + ) + } + } + } + } + + return@runWriteAction targetClass + } catch (_: IncorrectOperationException) { + showErrorLater(project, d.className) + return@runWriteAction null + } + } + } + } + + override fun toString(): String { + return KotlinBundle.message("intention.create.test.dialog.kotlin") + } + + companion object { + private fun createTestClass(d: CreateTestDialog): PsiElement? { + val testFrameworkDescriptor = d.selectedTestFrameworkDescriptor + val fileTemplateDescriptor = TestIntegrationUtils.MethodKind.TEST_CLASS.getFileTemplateDescriptor(testFrameworkDescriptor) + val targetDirectory = d.targetDirectory + + val aPackage = JavaDirectoryService.getInstance().getPackage(targetDirectory) + val className = d.className + var targetFile: KtFile? = null + if (aPackage != null) { + val scope = GlobalSearchScopesCore.directoryScope(targetDirectory, false) + targetFile = aPackage.getFiles(scope).firstOrNull { file -> + file is KtFile && file.name == "$className.${KotlinFileType.EXTENSION}" + } as? KtFile + // TODO: it could be that file is exists, but there is no such class declaration in it + } + if (targetFile == null && fileTemplateDescriptor != null) { + val element = createTestClassFromCodeTemplate(d, fileTemplateDescriptor, targetDirectory) + if (element is KtClass) return element + targetFile = element as? KtFile + } + + return targetFile?.declarations?.firstOrNull { it is KtClass && it.name == className }?.let { return it } + } + + private fun createTestClassFromCodeTemplate( + d: CreateTestDialog, + fileTemplateDescriptor: FileTemplateDescriptor, + targetDirectory: PsiDirectory + ): KtElement? { + val templateName = fileTemplateDescriptor.fileName + val project = targetDirectory.getProject() + val fileTemplate = FileTemplateManager.getInstance(project).getCodeTemplate(templateName) + val defaultProperties = FileTemplateManager.getInstance(project).getDefaultProperties() + val properties = Properties(defaultProperties) + properties.setProperty(FileTemplate.ATTRIBUTE_NAME, d.className) + val targetClass = d.targetClass + if (targetClass != null && targetClass.isValid()) { + properties.setProperty(FileTemplate.ATTRIBUTE_CLASS_NAME, targetClass.getQualifiedName()) + } + val fileName = d.className + KotlinFileType.DOT_DEFAULT_EXTENSION + return try { + val createdFromTemplate = FileTemplateUtil.createFromTemplate(fileTemplate, fileName, properties, targetDirectory) + createdFromTemplate as? KtElement + } catch (_: Exception) { + null + } + } + + private fun addSuperClass(targetClass: KtClass, project: Project, superClassName: String) { + val extendsList = targetClass.superTypeListEntries + val referenceElements = extendsList.flatMap { it.references.toList() } + val psiFactory = KtPsiFactory(project) + val superTypeEntry = psiFactory.createSuperTypeEntry(superClassName) + if (referenceElements.isEmpty()) { + val superTypeListEntry = + targetClass.addSuperTypeListEntry(superTypeEntry) + ShortenReferencesFacility.getInstance().shorten(superTypeListEntry) + } else { + referenceElements[0]!!.element.replace(superTypeEntry) + } + } + + @OptIn(KaAllowAnalysisOnEdt::class, KaAllowAnalysisFromWriteAction::class) + fun addTestMethods( + editor: Editor, + targetClass: KtClass, + sourceClass: PsiClass?, + descriptor: TestFramework, + methods: Collection, + generateBefore: Boolean, + generateAfter: Boolean + ) { + val existingNames: MutableSet = HashSet() + var anchor: KtNamedFunction? = null + if (generateBefore && descriptor.findSetUpMethod(targetClass) == null) { + anchor = allowAnalysisOnEdt { + allowAnalysisFromWriteAction { + KotlinGenerateTestSupportActionBase.doGenerate(editor, targetClass.containingFile, targetClass, descriptor, MethodKind.SET_UP) + } + } + } + + if (generateAfter && descriptor.findTearDownMethod(targetClass) == null) { + anchor = allowAnalysisOnEdt { + allowAnalysisFromWriteAction { + KotlinGenerateTestSupportActionBase.doGenerate(editor, targetClass.containingFile, targetClass, descriptor, MethodKind.TEAR_DOWN) + } + } + } + + val template = TestIntegrationUtils.createTestMethodTemplate( + MethodKind.TEST, descriptor, + targetClass, sourceClass, null, true, existingNames + ) + val elementFactory = JVMElementFactories.getFactory(targetClass.language, targetClass.getProject()) + val prefix = try { + elementFactory?.createMethodFromText(template.getTemplateText(), targetClass)?.getName() ?: "" + } catch (_: IncorrectOperationException) { + "" + } + + for (existingMethod in targetClass.declarations.filterIsInstance()) { + val name = existingMethod.getName() ?: continue + existingNames.add(StringUtil.decapitalize(name.removePrefix(prefix))) + } + + for (m in methods) { + anchor = generateMethod(descriptor, targetClass, sourceClass, editor, m.getMember().getName(), + existingNames, anchor) + } + } + + private fun showErrorLater(project: Project?, targetClassName: String) { + ApplicationManager.getApplication().invokeLater(Runnable { + Messages.showErrorDialog(project, + KotlinBundle.message("intention.error.cannot.create.class.message", targetClassName), + KotlinBundle.message("intention.error.cannot.create.class.title")) + }) + } + + private fun generateMethod( + descriptor: TestFramework?, + targetClass: KtClass, + sourceClass: PsiClass?, + editor: Editor, + name: String?, + existingNames: MutableSet?, + anchor: KtNamedFunction? + ): KtNamedFunction? { + val project = targetClass.getProject() + val psiFactory = KtPsiFactory(project) + val dummyFunction = psiFactory.createFunction("fun dummy() = Unit") + val function = insertMembersAfter(editor, targetClass, listOf(dummyFunction), anchor).firstOrNull()?.element ?: return null + PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument()) + TestIntegrationUtils.runTestMethodTemplate(MethodKind.TEST, + descriptor, editor, + targetClass, sourceClass, function, + function.modifierList ?: function + , name, + true, + existingNames) + return function + } + } +} \ No newline at end of file diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinCreateTestIntention.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinCreateTestIntention.kt index c7d6bfff6719..3a2e973a85f8 100644 --- a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinCreateTestIntention.kt +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/testIntegration/KotlinCreateTestIntention.kt @@ -2,12 +2,15 @@ package org.jetbrains.kotlin.idea.testIntegration import com.intellij.codeInsight.navigation.activateFileWithPsiElement +import com.intellij.ide.highlighter.JavaFileType import com.intellij.openapi.application.runWriteAction +import com.intellij.openapi.fileTypes.FileType import com.intellij.openapi.module.Module import com.intellij.openapi.options.advanced.AdvancedSettings import com.intellij.openapi.project.Project import com.intellij.psi.PsiClass import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiFile import com.intellij.psi.PsiJavaFile import org.jetbrains.kotlin.idea.actions.JavaToKotlinAction import org.jetbrains.kotlin.idea.base.facet.platform.platform @@ -28,13 +31,15 @@ class KotlinCreateTestIntention: AbstractKotlinCreateTestIntention() { override fun isApplicableForModule(module: Module): Boolean = !(module.platform.isJs() && !AdvancedSettings.getBoolean("kotlin.mpp.experimental")) - override fun convertJavaClass( + override fun convertClass( project: Project, generatedClass: PsiClass, existingClass: KtClassOrObject?, - generatedFile: PsiJavaFile, + generatedFile: PsiFile, srcModule: Module ) { + if (generatedFile !is PsiJavaFile || generatedClass.language != JavaFileType.INSTANCE) return + project.executeCommand( KotlinBundle.message("convert.class.0.to.kotlin", generatedClass.name.toString()), this diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/other/addActualToClass/jvm/jvm.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/other/addActualToClass/jvm/jvm.kt index 8601d1395060..b3496b00af29 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/other/addActualToClass/jvm/jvm.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/other/addActualToClass/jvm/jvm.kt @@ -1,7 +1,6 @@ // "Add 'actual' modifier" "false" // ACTION: Create test // ACTION: Rename file to Foo.kt -// IGNORE_K2 // KTIJ-19789 No diagnostic "actual is missing" on top-level class/function/property in IDE class Foo diff --git a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/other/addActualToTopLevelMember/jvm/jvm.kt b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/other/addActualToTopLevelMember/jvm/jvm.kt index 3d3c6eab9c35..5ed6a565e7b3 100644 --- a/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/other/addActualToTopLevelMember/jvm/jvm.kt +++ b/plugins/kotlin/idea/tests/testData/multiModuleQuickFix/other/addActualToTopLevelMember/jvm/jvm.kt @@ -1,6 +1,5 @@ // "Add 'actual' modifier" "false" // ACTION: Create test -// IGNORE_K2 // KTIJ-19789 No diagnostic "actual is missing" on top-level class/function/property in IDE fun foo() {} diff --git a/plugins/kotlin/kotlin.ide/src/org/jetbrains/kotlin/idea/actions/KotlinCreateFromTemplateHandler.kt b/plugins/kotlin/kotlin.ide/src/org/jetbrains/kotlin/idea/actions/KotlinCreateFromTemplateHandler.kt index c3c78e5a9654..69d519b24b84 100644 --- a/plugins/kotlin/kotlin.ide/src/org/jetbrains/kotlin/idea/actions/KotlinCreateFromTemplateHandler.kt +++ b/plugins/kotlin/kotlin.ide/src/org/jetbrains/kotlin/idea/actions/KotlinCreateFromTemplateHandler.kt @@ -4,11 +4,14 @@ package org.jetbrains.kotlin.idea.actions import com.intellij.ide.fileTemplates.DefaultCreateFromTemplateHandler import com.intellij.ide.fileTemplates.FileTemplate +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiDirectory +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded class KotlinCreateFromTemplateHandler : DefaultCreateFromTemplateHandler() { - override fun handlesTemplate(template: FileTemplate) = template.isTemplateOfType(KotlinFileType.INSTANCE) + override fun handlesTemplate(template: FileTemplate): Boolean = template.isTemplateOfType(KotlinFileType.INSTANCE) override fun prepareProperties(props: MutableMap) { val packageName = props[FileTemplate.ATTRIBUTE_PACKAGE_NAME] as? String @@ -21,4 +24,15 @@ class KotlinCreateFromTemplateHandler : DefaultCreateFromTemplateHandler() { props[FileTemplate.ATTRIBUTE_NAME] = name.quoteIfNeeded() } } + + override fun createFromTemplate( + project: Project, + directory: PsiDirectory, + fileName: String?, + template: FileTemplate, + templateText: String, + props: Map + ): PsiElement { + return super.createFromTemplate(project, directory, fileName, template, templateText, props) + } } \ No newline at end of file diff --git a/plugins/kotlin/kotlin.ide/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateActionBase.kt b/plugins/kotlin/kotlin.ide/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateActionBase.kt index 2740e1a1a454..91b28ad273c4 100644 --- a/plugins/kotlin/kotlin.ide/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateActionBase.kt +++ b/plugins/kotlin/kotlin.ide/src/org/jetbrains/kotlin/idea/actions/generate/KotlinGenerateActionBase.kt @@ -46,7 +46,7 @@ abstract class KotlinGenerateActionBase : CodeInsightAction(), CodeInsightAction protected abstract fun isValidForClass(targetClass: KtClassOrObject): Boolean - override fun startInWriteAction() = false + override fun startInWriteAction(): Boolean = false - override fun getHandler() = this + override fun getHandler(): KotlinGenerateActionBase = this } \ No newline at end of file diff --git a/plugins/kotlin/run-configurations/junit/kotlin.run-configurations.junit.iml b/plugins/kotlin/run-configurations/junit/kotlin.run-configurations.junit.iml index 937cf6502eb4..3ff6c753537a 100644 --- a/plugins/kotlin/run-configurations/junit/kotlin.run-configurations.junit.iml +++ b/plugins/kotlin/run-configurations/junit/kotlin.run-configurations.junit.iml @@ -22,5 +22,7 @@ + + \ No newline at end of file diff --git a/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit3 Test Class.kt.ft b/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit3 Test Class.kt.ft new file mode 100644 index 000000000000..ea04842d9d5d --- /dev/null +++ b/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit3 Test Class.kt.ft @@ -0,0 +1,5 @@ +import junit.framework.TestCase +#parse("File Header.java") +class ${NAME}: TestCase() { + ${BODY} +} \ No newline at end of file diff --git a/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit3 Test Class.kt.html b/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit3 Test Class.kt.html new file mode 100644 index 000000000000..02e8d3f6a7f6 --- /dev/null +++ b/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit3 Test Class.kt.html @@ -0,0 +1,31 @@ + + + + + + +
+ Creates a Kotlin JUnit 3 test class. +
+ + + + + + + + + + + + + + + + + + + +
Predefined variables take the following values:
${NAME} Name of the created class
${CLASS_NAME} Name of the tested class
${BODY} Generated class body
+ + \ No newline at end of file diff --git a/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit4 Test Class.kt.ft b/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit4 Test Class.kt.ft new file mode 100644 index 000000000000..8d722bac9c17 --- /dev/null +++ b/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit4 Test Class.kt.ft @@ -0,0 +1,5 @@ +import org.junit.Assert.* +#parse("File Header.java") +class ${NAME} { + ${BODY} +} \ No newline at end of file diff --git a/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit4 Test Class.kt.html b/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit4 Test Class.kt.html new file mode 100644 index 000000000000..f502e0be4908 --- /dev/null +++ b/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit4 Test Class.kt.html @@ -0,0 +1,31 @@ + + + + + + +
+ Creates a Kotlin JUnit 4 test class. +
+ + + + + + + + + + + + + + + + + + + +
Predefined variables take the following values:
${NAME} Name of the created class
${CLASS_NAME} Name of the tested class
${BODY} Generated class body
+ + \ No newline at end of file diff --git a/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit5 Test Class.kt.ft b/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit5 Test Class.kt.ft new file mode 100644 index 000000000000..75790f28544c --- /dev/null +++ b/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit5 Test Class.kt.ft @@ -0,0 +1,5 @@ +import org.junit.jupiter.api.Assertions.* +#parse("File Header.java") +class ${NAME} { + ${BODY} +} \ No newline at end of file diff --git a/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit5 Test Class.kt.html b/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit5 Test Class.kt.html new file mode 100644 index 000000000000..240c24d34693 --- /dev/null +++ b/plugins/kotlin/run-configurations/junit/resources/fileTemplates/code/Kotlin JUnit5 Test Class.kt.html @@ -0,0 +1,31 @@ + + + + + + +
+ Creates a Kotlin JUnit 5 test class. +
+ + + + + + + + + + + + + + + + + + + +
Predefined variables take the following values:
${NAME} Name of the created class
${CLASS_NAME} Name of the tested class
${BODY} Generated class body
+ + \ No newline at end of file diff --git a/plugins/kotlin/run-configurations/junit/src/org/jetbrains/kotlin/idea/junit/KotlinJUnit3Framework.kt b/plugins/kotlin/run-configurations/junit/src/org/jetbrains/kotlin/idea/junit/KotlinJUnit3Framework.kt index 0778557eab31..6374a1056343 100644 --- a/plugins/kotlin/run-configurations/junit/src/org/jetbrains/kotlin/idea/junit/KotlinJUnit3Framework.kt +++ b/plugins/kotlin/run-configurations/junit/src/org/jetbrains/kotlin/idea/junit/KotlinJUnit3Framework.kt @@ -233,17 +233,17 @@ class KotlinJUnit3Framework: JUnit3Framework(), KotlinPsiBasedTestFramework { override fun isIgnoredMethod(declaration: KtNamedFunction): Boolean = psiBasedDelegate.isIgnoredMethod(declaration) - override fun getSetUpMethodFileTemplateDescriptor(): FileTemplateDescriptor? { - return FileTemplateDescriptor("Kotlin JUnit3 SetUp Function.kt") - } + override fun getSetUpMethodFileTemplateDescriptor(): FileTemplateDescriptor? = + FileTemplateDescriptor("Kotlin JUnit3 SetUp Function.kt") - override fun getTearDownMethodFileTemplateDescriptor(): FileTemplateDescriptor? { - return FileTemplateDescriptor("Kotlin JUnit3 TearDown Function.kt") - } + override fun getTearDownMethodFileTemplateDescriptor(): FileTemplateDescriptor? = + FileTemplateDescriptor("Kotlin JUnit3 TearDown Function.kt") - override fun getTestMethodFileTemplateDescriptor(): FileTemplateDescriptor { - return FileTemplateDescriptor("Kotlin JUnit3 Test Function.kt") - } + override fun getTestMethodFileTemplateDescriptor(): FileTemplateDescriptor = + FileTemplateDescriptor("Kotlin JUnit3 Test Function.kt") + + override fun getTestClassFileTemplateDescriptor(): FileTemplateDescriptor? = + FileTemplateDescriptor("Kotlin JUnit3 Test Class.kt") } private val TEST_CLASS_FQN = setOf(JUnitUtil.TEST_CASE_CLASS) \ No newline at end of file diff --git a/plugins/kotlin/run-configurations/junit/src/org/jetbrains/kotlin/idea/junit/KotlinJUnit4Framework.kt b/plugins/kotlin/run-configurations/junit/src/org/jetbrains/kotlin/idea/junit/KotlinJUnit4Framework.kt index 90693daeb54a..e7b953ea893c 100644 --- a/plugins/kotlin/run-configurations/junit/src/org/jetbrains/kotlin/idea/junit/KotlinJUnit4Framework.kt +++ b/plugins/kotlin/run-configurations/junit/src/org/jetbrains/kotlin/idea/junit/KotlinJUnit4Framework.kt @@ -186,6 +186,9 @@ class KotlinJUnit4Framework: JUnit4Framework(), KotlinPsiBasedTestFramework { return FileTemplateDescriptor("Kotlin JUnit4 Parameters Function.kt") } + override fun getTestClassFileTemplateDescriptor(): FileTemplateDescriptor? = + FileTemplateDescriptor("Kotlin JUnit4 Test Class.kt") + override fun isFrameworkAvailable(clazz: PsiElement): Boolean { return super.isFrameworkAvailable(clazz) || clazz is KtClass && psiBasedDelegate.isFrameworkAvailable(clazz) } diff --git a/plugins/kotlin/run-configurations/junit/src/org/jetbrains/kotlin/idea/junit/KotlinJUnit5Framework.kt b/plugins/kotlin/run-configurations/junit/src/org/jetbrains/kotlin/idea/junit/KotlinJUnit5Framework.kt index 80b44b8110ee..2378c1719ffb 100644 --- a/plugins/kotlin/run-configurations/junit/src/org/jetbrains/kotlin/idea/junit/KotlinJUnit5Framework.kt +++ b/plugins/kotlin/run-configurations/junit/src/org/jetbrains/kotlin/idea/junit/KotlinJUnit5Framework.kt @@ -163,6 +163,9 @@ class KotlinJUnit5Framework: JUnit5Framework(), KotlinPsiBasedTestFramework { override fun getTestMethodFileTemplateDescriptor(): FileTemplateDescriptor { return FileTemplateDescriptor("Kotlin JUnit5 Test Function.kt") } + + override fun getTestClassFileTemplateDescriptor(): FileTemplateDescriptor? = + FileTemplateDescriptor("Kotlin JUnit5 Test Class.kt") } private val METHOD_ANNOTATION_FQN = setOf( diff --git a/plugins/kotlin/run-configurations/testng/kotlin.run-configurations.testng.iml b/plugins/kotlin/run-configurations/testng/kotlin.run-configurations.testng.iml index be84fd01cd5a..ffdf9d464eb6 100644 --- a/plugins/kotlin/run-configurations/testng/kotlin.run-configurations.testng.iml +++ b/plugins/kotlin/run-configurations/testng/kotlin.run-configurations.testng.iml @@ -23,5 +23,6 @@ + \ No newline at end of file diff --git a/plugins/kotlin/run-configurations/testng/resources/fileTemplates/code/Kotlin TestNG Test Class.kt.ft b/plugins/kotlin/run-configurations/testng/resources/fileTemplates/code/Kotlin TestNG Test Class.kt.ft new file mode 100644 index 000000000000..0c87abbd39f1 --- /dev/null +++ b/plugins/kotlin/run-configurations/testng/resources/fileTemplates/code/Kotlin TestNG Test Class.kt.ft @@ -0,0 +1,5 @@ +import org.testng.Assert.* +#parse("File Header.java") +class ${NAME} { + ${BODY} +} \ No newline at end of file diff --git a/plugins/kotlin/run-configurations/testng/resources/fileTemplates/code/Kotlin TestNG Test Class.kt.html b/plugins/kotlin/run-configurations/testng/resources/fileTemplates/code/Kotlin TestNG Test Class.kt.html new file mode 100644 index 000000000000..825775eef96a --- /dev/null +++ b/plugins/kotlin/run-configurations/testng/resources/fileTemplates/code/Kotlin TestNG Test Class.kt.html @@ -0,0 +1,31 @@ + + + + + + +
+ Creates a Kotlin TestNG test class. +
+ + + + + + + + + + + + + + + + + + + +
Predefined variables take the following values:
${NAME} Name of the created class
${CLASS_NAME} Name of the tested class
${BODY} Generated class body
+ + \ No newline at end of file diff --git a/plugins/kotlin/run-configurations/testng/src/org/jetbrains/kotlin/idea/testng/KotlinTestNGFramework.kt b/plugins/kotlin/run-configurations/testng/src/org/jetbrains/kotlin/idea/testng/KotlinTestNGFramework.kt index dde0425e362d..5c23dfee18e0 100644 --- a/plugins/kotlin/run-configurations/testng/src/org/jetbrains/kotlin/idea/testng/KotlinTestNGFramework.kt +++ b/plugins/kotlin/run-configurations/testng/src/org/jetbrains/kotlin/idea/testng/KotlinTestNGFramework.kt @@ -9,27 +9,21 @@ import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.parentOfType import com.intellij.util.ThreeState -import com.intellij.util.ThreeState.NO -import com.intellij.util.ThreeState.UNSURE -import com.intellij.util.ThreeState.YES +import com.intellij.util.ThreeState.* import com.theoryinpractice.testng.TestNGFramework import com.theoryinpractice.testng.util.TestNGUtil import org.jetbrains.kotlin.asJava.elements.KtLightElement import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.idea.base.plugin.KotlinPluginModeProvider import org.jetbrains.kotlin.idea.testIntegration.framework.AbstractKotlinPsiBasedTestFramework import org.jetbrains.kotlin.idea.testIntegration.framework.KotlinPsiBasedTestFramework import org.jetbrains.kotlin.idea.testIntegration.framework.KotlinPsiBasedTestFramework.Companion.asKtClassOrObject import org.jetbrains.kotlin.idea.testIntegration.framework.KotlinPsiBasedTestFramework.Companion.asKtNamedFunction import org.jetbrains.kotlin.lexer.KtTokens -import org.jetbrains.kotlin.psi.KtClass -import org.jetbrains.kotlin.psi.KtClassOrObject -import org.jetbrains.kotlin.psi.KtNamedDeclaration -import org.jetbrains.kotlin.psi.KtNamedFunction -import org.jetbrains.kotlin.psi.KtSuperTypeCallEntry +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getParentOfType import org.jetbrains.kotlin.psi.psiUtil.isPrivate import org.jetbrains.kotlin.utils.addToStdlib.safeAs -import kotlin.collections.plus class KotlinTestNGFramework: TestNGFramework(), KotlinPsiBasedTestFramework { private val psiBasedDelegate = object : AbstractKotlinPsiBasedTestFramework() { @@ -191,6 +185,13 @@ class KotlinTestNGFramework: TestNGFramework(), KotlinPsiBasedTestFramework { return FileTemplateDescriptor("Kotlin TestNG Test Function.kt") } + override fun getTestClassFileTemplateDescriptor(): FileTemplateDescriptor? = + if (KotlinPluginModeProvider.isK1Mode()) { + super.getTestClassFileTemplateDescriptor() + } else { + FileTemplateDescriptor("Kotlin TestNG Test Class.kt") + } + override fun getParametersMethodFileTemplateDescriptor(): FileTemplateDescriptor? { return FileTemplateDescriptor("Kotlin TestNG Parameters Function.kt") }