Create header implementation fix: check import directive, change / add it if needed

This commit is contained in:
Mikhail Glukhikh
2017-01-11 15:31:38 +03:00
parent f33d421bab
commit aeb6bff145
16 changed files with 115 additions and 10 deletions
@@ -30,13 +30,11 @@ 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.core.*
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.createKotlinFile
import org.jetbrains.kotlin.idea.refactoring.getOrCreateKotlinFile
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.application.runWriteAction
@@ -66,6 +64,20 @@ sealed class CreateHeaderImplementationFix<out D : KtNamedDeclaration>(
val generated = factory.generateIt(project, element) ?: return
runWriteAction {
if (implFile.packageDirective?.fqName != file.packageDirective?.fqName &&
implFile.declarations.isEmpty()) {
val packageDirective = file.packageDirective
packageDirective?.let {
val oldPackageDirective = implFile.packageDirective
val newPackageDirective = factory.createPackageDirective(it.fqName)
if (oldPackageDirective != null) {
oldPackageDirective.replace(newPackageDirective)
}
else {
implFile.add(newPackageDirective)
}
}
}
val implDeclaration = implFile.add(generated) as KtElement
val reformatted = CodeStyleManager.getInstance(project).reformat(implDeclaration)
ShortenReferences.DEFAULT.process(reformatted as KtElement)
@@ -93,7 +105,28 @@ sealed class CreateHeaderImplementationFix<out D : KtNamedDeclaration>(
implModule, headerPackage?.qualifiedName ?: "", null, false
) ?: return null
return runWriteAction {
getOrCreateKotlinFile("$name.kt", implDirectory)
val fileName = "$name.kt"
val existingFile = implDirectory.findFile(fileName)
val packageDirective = declaration.containingKtFile.packageDirective
val packageName =
if (packageDirective?.packageNameExpression == null) implDirectory.getPackage()?.qualifiedName
else packageDirective.fqName.asString()
if (existingFile is KtFile) {
val existingPackageDirective = existingFile.packageDirective
if (existingFile.declarations.isNotEmpty() &&
existingPackageDirective?.fqName != packageDirective?.fqName) {
val newName = KotlinNameSuggester.suggestNameByName(name) {
implDirectory.findFile("$it.kt") == null
} + ".kt"
createKotlinFile(newName, implDirectory, packageName)
}
else {
existingFile
}
}
else {
createKotlinFile(fileName, implDirectory, packageName)
}
}
}
@@ -0,0 +1,6 @@
// "Create header class implementation for platform JVM" "true"
// SHOULD_BE_AVAILABLE_AFTER_EXECUTION
package test.inner
header class <caret>My
@@ -0,0 +1,6 @@
// "Create header class implementation for platform JVM" "true"
// SHOULD_BE_AVAILABLE_AFTER_EXECUTION
package test.inner
header class My
@@ -0,0 +1,3 @@
package test.inner
impl class My
@@ -0,0 +1,6 @@
// "Create header class implementation for platform JVM" "true"
// SHOULD_BE_AVAILABLE_AFTER_EXECUTION
package test.inner
header class <caret>My
@@ -0,0 +1,6 @@
// "Create header class implementation for platform JVM" "true"
// SHOULD_BE_AVAILABLE_AFTER_EXECUTION
package test.inner
header class My
@@ -0,0 +1,3 @@
package incorrect
class Some
@@ -0,0 +1,3 @@
package incorrect
class Some
@@ -0,0 +1,3 @@
package test.inner
impl class My
@@ -0,0 +1,6 @@
// "Create header class implementation for platform JVM" "true"
// SHOULD_BE_AVAILABLE_AFTER_EXECUTION
package test.inner
header class <caret>My
@@ -0,0 +1,6 @@
// "Create header class implementation for platform JVM" "true"
// SHOULD_BE_AVAILABLE_AFTER_EXECUTION
package test.inner
header class My
@@ -0,0 +1 @@
package incorrect
@@ -0,0 +1,3 @@
package test.inner
impl class My
@@ -26,10 +26,12 @@ 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.refactoring.createKotlinFile
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.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.io.File
import java.util.regex.Pattern
@@ -40,7 +42,8 @@ abstract class AbstractQuickFixMultiModuleTest : AbstractMultiModuleTest() {
override fun getTestDataPath() = testPath
protected fun shouldBeAvailableAfterExecution() = false
protected fun shouldBeAvailableAfterExecution(file: KtFile) =
InTextDirectivesUtils.isDirectiveDefined(file.text, "// SHOULD_BE_AVAILABLE_AFTER_EXECUTION")
private fun getActionsTexts(availableActions: List<IntentionAction>) = availableActions.map { it.text }
@@ -68,7 +71,7 @@ abstract class AbstractQuickFixMultiModuleTest : AbstractMultiModuleTest() {
DirectiveBasedActionUtils.checkForUnexpectedErrors(psiFile)
}
doAction(text, actionShouldBeAvailable, actionFileName)
doAction(text, actionShouldBeAvailable, actionFileName, actionFile)
if (actionShouldBeAvailable) {
val testDirectory = File(testPath)
@@ -79,7 +82,9 @@ abstract class AbstractQuickFixMultiModuleTest : AbstractMultiModuleTest() {
try {
val editedFile = allFilesInProject.find {
it.name.toLowerCase() == file.name.removeSuffix(".after").toLowerCase()
}!!
} ?: allFilesInProject.mapNotNull {
it.containingDirectory?.findFile(file.name.removeSuffix(".after"))
}.single()
setActiveEditor(editedFile.findExistingEditor() ?: createEditor(editedFile.virtualFile))
checkResultByFile(file.relativeTo(testDirectory).path)
}
@@ -105,7 +110,7 @@ abstract class AbstractQuickFixMultiModuleTest : AbstractMultiModuleTest() {
// TODO: merge with AbstractQuickFixMultiFileTest
fun doAction(text: String, actionShouldBeAvailable: Boolean, testFilePath: String) {
fun doAction(text: String, actionShouldBeAvailable: Boolean, testFilePath: String, actionFile: KtFile) {
val pattern = if (text.startsWith("/"))
Pattern.compile(text.substring(1, text.length - 1))
else
@@ -140,7 +145,7 @@ abstract class AbstractQuickFixMultiModuleTest : AbstractMultiModuleTest() {
UIUtil.dispatchAllInvocationEvents()
if (!shouldBeAvailableAfterExecution()) {
if (!shouldBeAvailableAfterExecution(actionFile)) {
val afterAction = findActionByPattern(pattern, getAvailableActions())
if (afterAction != null) {
@@ -65,6 +65,21 @@ class QuickFixMultiModuleTest : AbstractQuickFixMultiModuleTest() {
doMultiPlatformTest()
}
@Test
fun testPackage() {
doMultiPlatformTest()
}
@Test
fun testPackageIncorrect() {
doMultiPlatformTest()
}
@Test
fun testPackageIncorrectEmpty() {
doMultiPlatformTest()
}
@Test
fun testNested() {
doMultiPlatformTest()