mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[kotlin] KTIJ-35016 K2 Move: support file suffixes for expect/actual
Files in different source sets by convention should have different names. Mostly this is due to JVM limitations: file facade classes with the same name can lead to clashes. Move changes: — File destination field is treated as file name in the common source set for expect/actual move — Iff expect/actual move setting is enabled, platform file suffixes are added in different source sets UI changes: — Show expect/actual setting only for declaration movement with expects/actuals — Add an explanation for the file destination in case of expect/actual move — Add a warning for platform file suffix used for common file name — Disable dialog button when trying to move multiple declarations to the same file GitOrigin-RevId: 9c294d146366f55eed10fffc8e23cbefb8ebe0cf
This commit is contained in:
committed by
intellij-monorepo-bot
parent
c9d485e340
commit
ed4d01ff3b
@@ -48,6 +48,9 @@ visibility.combo.title=&Visibility:
|
||||
moveInstanceMethod.select.an.instance.parameter=Select an &instance expression:
|
||||
choose.destination.package=Choose Destination Package
|
||||
create.explicit.package.warning=Implicit package ''{0}'' will become explicit
|
||||
move.added.kmp.suffixes.warning.0=Platform suffix ''.{0}'' will be copied across multiple source sets, consider removing it
|
||||
move.added.kmp.suffixes.info=Multiplatform file suffixes will be added automatically
|
||||
tooltip.text.move.kmp.target.file=Base file name in the common source set. Platform source sets will receive suffixes in accordance with the multiplatform conventions.
|
||||
interface.name.prompt=&Interface name:
|
||||
package.for.new.interface=&Package for new interface:
|
||||
pull.up.members.to=P&ull up members of {0} to:
|
||||
|
||||
@@ -889,8 +889,8 @@ parameter.name.is.invalid=Parameter name ''{0}'' is invalid
|
||||
parameter.type.is.invalid=Parameter type ''{0}'' is invalid
|
||||
parameter.types.are.not.denotable=Cannot extract method since following types are not denotable in the target scope:
|
||||
refactoring.move.non.kotlin.file=Target must be a Kotlin file
|
||||
refactoring.class.destination=Class destination
|
||||
refactoring.file.destination=File destination
|
||||
refactoring.class.destination=Class destination:
|
||||
refactoring.file.destination=File destination:
|
||||
refactoring.cannot.find.target.class=Cannot find target class
|
||||
return.type.is.invalid=Return type is invalid
|
||||
searching.usages.of.0.parameter=Searching usages of ''{0}''parameter
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="FacetManager">
|
||||
<facet type="kotlin-language" name="Kotlin">
|
||||
<configuration version="3" platform="Common (experimental) " useProjectSettings="false">
|
||||
<compilerSettings />
|
||||
<compilerArguments>
|
||||
<option name="multiPlatform" value="true" />
|
||||
</compilerArguments>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package bar
|
||||
|
||||
expect fun foo(): Int
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package foo
|
||||
|
||||
fun regularCommon() {
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="commonMain" scope="COMPILE" />
|
||||
</component>
|
||||
<component name="FacetManager">
|
||||
<facet type="kotlin-language" name="Kotlin">
|
||||
<configuration version="3" platform="JavaScript " useProjectSettings="false">
|
||||
<implements>commonMain</implements>
|
||||
<compilerSettings />
|
||||
<compilerArguments>
|
||||
<option name="multiPlatform" value="true" />
|
||||
</compilerArguments>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package bar
|
||||
|
||||
fun existingInTargetPackageJs() {
|
||||
}
|
||||
|
||||
actual fun foo(): Int = 1
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package foo
|
||||
|
||||
fun regularJs() {
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="commonMain" scope="COMPILE" />
|
||||
</component>
|
||||
<component name="FacetManager">
|
||||
<facet type="kotlin-language" name="Kotlin">
|
||||
<configuration version="3" platform="JVM 1.8" useProjectSettings="false">
|
||||
<implements>commonMain</implements>
|
||||
<compilerSettings />
|
||||
<compilerArguments>
|
||||
<option name="multiPlatform" value="true" />
|
||||
</compilerArguments>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package bar
|
||||
|
||||
actual fun fo<caret>o(): Int = 0
|
||||
|
||||
fun regularJvm() {
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="FacetManager">
|
||||
<facet type="kotlin-language" name="Kotlin">
|
||||
<configuration version="3" platform="Common (experimental) " useProjectSettings="false">
|
||||
<compilerSettings />
|
||||
<compilerArguments>
|
||||
<option name="multiPlatform" value="true" />
|
||||
</compilerArguments>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package foo
|
||||
|
||||
expect fun foo(): Int
|
||||
|
||||
fun regularCommon() {
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="commonMain" scope="COMPILE" />
|
||||
</component>
|
||||
<component name="FacetManager">
|
||||
<facet type="kotlin-language" name="Kotlin">
|
||||
<configuration version="3" platform="JavaScript " useProjectSettings="false">
|
||||
<implements>commonMain</implements>
|
||||
<compilerSettings />
|
||||
<compilerArguments>
|
||||
<option name="multiPlatform" value="true" />
|
||||
</compilerArguments>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package bar
|
||||
|
||||
fun existingInTargetPackageJs() {
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package foo
|
||||
|
||||
actual fun foo(): Int = 1
|
||||
|
||||
fun regularJs() {
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="commonMain" scope="COMPILE" />
|
||||
</component>
|
||||
<component name="FacetManager">
|
||||
<facet type="kotlin-language" name="Kotlin">
|
||||
<configuration version="3" platform="JVM 1.8" useProjectSettings="false">
|
||||
<implements>commonMain</implements>
|
||||
<compilerSettings />
|
||||
<compilerArguments>
|
||||
<option name="multiPlatform" value="true" />
|
||||
</compilerArguments>
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
</module>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package foo
|
||||
|
||||
actual fun fo<caret>o(): Int = 0
|
||||
|
||||
fun reg<caret>ularJvm() {
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"mainFile": "jvmMain/src/foo/base.jvm.kt",
|
||||
"targetPackage": "bar",
|
||||
"withRuntime": "true",
|
||||
"modulesWithCommonRuntime": ["commonMain"],
|
||||
"modulesWithRuntime": ["jvmMain"],
|
||||
"modulesWithJsRuntime": ["jsMain"]
|
||||
}
|
||||
@@ -58,6 +58,8 @@ jvm_library(
|
||||
"//plugins/kotlin/base/facet",
|
||||
"//platform/observable",
|
||||
"//java/openapi:java",
|
||||
"@lib//:kotlinc-analysis-api-platform-interface",
|
||||
"//plugins/kotlin/base/external-build-system:kotlin-base-externalSystem",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -115,6 +117,8 @@ jvm_library(
|
||||
"//plugins/kotlin/base/facet",
|
||||
"//platform/observable",
|
||||
"//java/openapi:java",
|
||||
"@lib//:kotlinc-analysis-api-platform-interface",
|
||||
"//plugins/kotlin/base/external-build-system:kotlin-base-externalSystem",
|
||||
]
|
||||
)
|
||||
### auto-generated section `build intellij.kotlin.refactorings.move` end
|
||||
|
||||
+2
@@ -68,5 +68,7 @@
|
||||
<orderEntry type="module" module-name="intellij.kotlin.base.facet" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.observable" />
|
||||
<orderEntry type="module" module-name="intellij.java" />
|
||||
<orderEntry type="library" name="kotlinc.analysis-api-platform-interface" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.kotlin.base.externalSystem" />
|
||||
</component>
|
||||
</module>
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
<idea-plugin visibility="internal" package="org.jetbrains.kotlin.idea.k2.refactoring.move">
|
||||
<dependencies>
|
||||
<module name="intellij.kotlin.searching"/>
|
||||
<module name="intellij.kotlin.base.externalSystem"/>
|
||||
</dependencies>
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
<refactoring.moveHandler
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// Copyright 2000-2025 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.refactoring.move.ui
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.observable.properties.ObservableBooleanProperty
|
||||
import com.intellij.openapi.observable.properties.ObservableProperty
|
||||
|
||||
open class ConstantObservableProperty<T>(private val value: T) : ObservableProperty<T> {
|
||||
final override fun get(): T = value
|
||||
final override fun afterChange(listener: (T) -> Unit) {}
|
||||
final override fun afterChange(parentDisposable: Disposable?, listener: (T) -> Unit) {}
|
||||
}
|
||||
|
||||
class ConstantBooleanObservableProperty(value: Boolean) : ConstantObservableProperty<Boolean>(value), ObservableBooleanProperty {
|
||||
override fun afterSet(parentDisposable: Disposable?, listener: () -> Unit) {}
|
||||
override fun afterReset(parentDisposable: Disposable?, listener: () -> Unit) {}
|
||||
}
|
||||
+47
-23
@@ -4,7 +4,8 @@ package org.jetbrains.kotlin.idea.k2.refactoring.move.ui
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.observable.properties.AtomicBooleanProperty
|
||||
import com.intellij.openapi.observable.properties.MutableBooleanProperty
|
||||
import com.intellij.openapi.observable.properties.ObservableBooleanProperty
|
||||
import com.intellij.openapi.observable.properties.ObservableProperty
|
||||
import com.intellij.openapi.observable.util.transform
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.roots.ProjectFileIndex
|
||||
import com.intellij.openapi.util.NlsContexts
|
||||
@@ -33,15 +34,13 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isExpectDeclaration
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
|
||||
|
||||
/**
|
||||
* @see K2MoveDescriptor
|
||||
*/
|
||||
sealed class K2MoveModel(observableUiSettings: ObservableUiSettings) : K2MoveModelObservableSettings {
|
||||
sealed class K2MoveModel(private val observableUiSettings: ObservableUiSettings) : K2MoveModelObservableSettings {
|
||||
init {
|
||||
observableUiSettings.registerK2MoveModelSettings(this)
|
||||
}
|
||||
@@ -76,6 +75,10 @@ sealed class K2MoveModel(observableUiSettings: ObservableUiSettings) : K2MoveMod
|
||||
}
|
||||
|
||||
open fun buildPanel(panel: Panel): Unit = with(panel) {
|
||||
val mppDeclarationSelectedObservable = observableUiSettings.mppDeclarationsSelectedObservable.transform { isSelected ->
|
||||
isSelected && inSourceRoot
|
||||
}
|
||||
|
||||
row {
|
||||
panel {
|
||||
searchForText.createComboBox(this)
|
||||
@@ -83,21 +86,25 @@ sealed class K2MoveModel(observableUiSettings: ObservableUiSettings) : K2MoveMod
|
||||
}.align(AlignY.TOP + AlignX.LEFT)
|
||||
panel {
|
||||
searchInComments.createComboBox(this)
|
||||
mppDeclarations.createComboBox(this, inSourceRoot && this@K2MoveModel is Declarations)
|
||||
mppDeclarations.createComboBox(
|
||||
panel = this,
|
||||
visible = mppDeclarationSelectedObservable,
|
||||
enabled = mppDeclarationSelectedObservable,
|
||||
)
|
||||
}.align(AlignY.TOP + AlignX.RIGHT)
|
||||
}
|
||||
}
|
||||
|
||||
override val mppDeclarationsObservable: ObservableBooleanProperty
|
||||
override val mppDeclarationsSettingObservable: ObservableProperty<Boolean>
|
||||
get() = mppDeclarations.observableProperty
|
||||
|
||||
override val searchForTextObservable: ObservableBooleanProperty
|
||||
override val searchForTextSettingObservable: ObservableProperty<Boolean>
|
||||
get() = searchForText.observableProperty
|
||||
|
||||
override val searchInCommentsObservable: ObservableBooleanProperty
|
||||
override val searchInCommentsSettingObservable: ObservableProperty<Boolean>
|
||||
get() = searchInComments.observableProperty
|
||||
|
||||
override val searchReferencesObservable: ObservableBooleanProperty
|
||||
override val searchReferencesSettingObservable: ObservableProperty<Boolean>
|
||||
get() = searchReferences.observableProperty
|
||||
|
||||
enum class Setting(private val text: @NlsContexts.Checkbox String) {
|
||||
@@ -143,19 +150,36 @@ sealed class K2MoveModel(observableUiSettings: ObservableUiSettings) : K2MoveMod
|
||||
};
|
||||
|
||||
abstract var state: Boolean
|
||||
// lazy prevents service access from constructor
|
||||
// lazy to avoid service access from constructor
|
||||
internal val observableProperty: MutableBooleanProperty by lazy { AtomicBooleanProperty(state) }
|
||||
|
||||
fun createComboBox(panel: Panel, enabled: Boolean = true) {
|
||||
fun createComboBox(
|
||||
panel: Panel,
|
||||
enabled: Boolean = true,
|
||||
visible: Boolean = true,
|
||||
) {
|
||||
createComboBox(
|
||||
panel = panel,
|
||||
enabled = ConstantBooleanObservableProperty(enabled),
|
||||
visible = ConstantBooleanObservableProperty(visible)
|
||||
)
|
||||
}
|
||||
|
||||
fun createComboBox(
|
||||
panel: Panel,
|
||||
enabled: ObservableProperty<Boolean>,
|
||||
visible: ObservableProperty<Boolean>,
|
||||
) {
|
||||
panel.row {
|
||||
val checkBox = checkBox(text).enabled(enabled)
|
||||
if (enabled) {
|
||||
checkBox.bindSelected(::state)
|
||||
// bind doesn't register state changes before dialog confirmation, but the UI should be updated on all state changes
|
||||
checkBox.onChanged { observableProperty.set(it.isSelected) }
|
||||
} else {
|
||||
checkBox.selected(false)
|
||||
}
|
||||
val checkBox = checkBox(text).enabledIf(enabled).visibleIf(visible)
|
||||
checkBox.onChanged { observableProperty.set(it.isSelected) }
|
||||
// reset only is intentional
|
||||
enabled.afterChange { isEnabled -> if (!isEnabled) checkBox.selected(false) }
|
||||
checkBox.bindSelected(::state)
|
||||
// value set in bindSelected doesn't trigger listeners
|
||||
val initial = enabled.get() && state
|
||||
checkBox.selected(initial)
|
||||
observableProperty.set(initial)
|
||||
}.layout(RowLayout.PARENT_GRID)
|
||||
}
|
||||
}
|
||||
@@ -226,7 +250,7 @@ sealed class K2MoveModel(observableUiSettings: ObservableUiSettings) : K2MoveMod
|
||||
&& name == fileName
|
||||
}
|
||||
if (!fileName.isValidKotlinFile()) return false
|
||||
val files = source.elements.map { it.containingFile }
|
||||
val files = source.elements.map { it.containingFile }.toSet()
|
||||
return files.size != 1 || !(files.single() as KtFile).isTargetFile() || target.isMoveToExplicitPackage()
|
||||
}
|
||||
|
||||
@@ -258,7 +282,7 @@ sealed class K2MoveModel(observableUiSettings: ObservableUiSettings) : K2MoveMod
|
||||
val searchForReferences = if (inSourceRoot) searchReferences.state else false
|
||||
val searchForText = searchForText.state
|
||||
val searchInComments = searchInComments.state
|
||||
if (mppDeclarations.state && declarations.any { it.isExpectDeclaration() || it.hasActualModifier() }) {
|
||||
if (mppDeclarations.state && declarations.any { it.isExpectOrActual() }) {
|
||||
val descriptors = declarations.flatMap { elem ->
|
||||
ExpectActualUtils.withExpectedActuals(elem).filterIsInstance<KtNamedDeclaration>()
|
||||
}.groupBy { elem ->
|
||||
@@ -340,7 +364,7 @@ sealed class K2MoveModel(observableUiSettings: ObservableUiSettings) : K2MoveMod
|
||||
|| declarationsFromFiles.isEmpty()
|
||||
|| (targetContainer is PsiDirectory && targetContainer.getPackage() == null) -> {
|
||||
// this move can contain foreign language files
|
||||
val source = K2MoveSourceModel.FileSource(elementsToMove.toFileElements().toSet())
|
||||
val source = K2MoveSourceModel.FileSource(elementsToMove.toFileElements().toSet(), observableUiSettings)
|
||||
val target = if (targetContainer is PsiDirectory) {
|
||||
val pkg = targetContainer.getFqNameWithImplicitPrefixOrRoot()
|
||||
K2MoveTargetModel.SourceDirectory(
|
||||
@@ -360,7 +384,7 @@ sealed class K2MoveModel(observableUiSettings: ObservableUiSettings) : K2MoveMod
|
||||
}
|
||||
|
||||
targetContainer is KtFile || targetContainer.isSingleClassContainer() || isSingleFileMove(elementsToMove) -> {
|
||||
val source = K2MoveSourceModel.ElementSource(declarationsFromFiles.toSet())
|
||||
val source = K2MoveSourceModel.ElementSource(declarationsFromFiles.toSet(), observableUiSettings)
|
||||
val targetFile = targetContainer?.containingFile
|
||||
val target = if (targetFile is KtFile) {
|
||||
K2MoveTargetModel.File(targetFile, observableUiSettings)
|
||||
|
||||
+30
-8
@@ -2,6 +2,9 @@
|
||||
package org.jetbrains.kotlin.idea.k2.refactoring.move.ui
|
||||
|
||||
import com.intellij.openapi.actionSystem.ex.ActionUtil
|
||||
import com.intellij.openapi.observable.properties.AtomicBooleanProperty
|
||||
import com.intellij.openapi.observable.properties.MutableBooleanProperty
|
||||
import com.intellij.openapi.observable.properties.ObservableBooleanProperty
|
||||
import com.intellij.platform.backend.presentation.TargetPresentation
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFileSystemItem
|
||||
@@ -13,6 +16,7 @@ import com.intellij.ui.dsl.builder.BottomGap
|
||||
import com.intellij.ui.dsl.builder.Panel
|
||||
import com.intellij.ui.dsl.builder.TopGap
|
||||
import com.intellij.ui.list.createTargetPresentationRenderer
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.k2.refactoring.move.descriptor.K2MoveSourceDescriptor
|
||||
import org.jetbrains.kotlin.idea.refactoring.memberInfo.AbstractKotlinMemberInfoModel
|
||||
@@ -23,15 +27,25 @@ import org.jetbrains.kotlin.psi.KtDeclarationContainer
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import javax.swing.JComponent
|
||||
|
||||
sealed interface K2MoveSourceModel<T : PsiElement> {
|
||||
val elements: Set<T>
|
||||
sealed class K2MoveSourceModel<T : PsiElement>(
|
||||
observableUiSettings: ObservableUiSettings
|
||||
): K2SourceModelObservableSettings {
|
||||
abstract val elements: Set<T>
|
||||
|
||||
fun toDescriptor(): K2MoveSourceDescriptor<T>?
|
||||
abstract fun toDescriptor(): K2MoveSourceDescriptor<T>?
|
||||
|
||||
fun buildPanel(panel: Panel, onError: (String?, JComponent) -> Unit, revalidateButtons: () -> Unit)
|
||||
abstract fun buildPanel(panel: Panel, onError: (String?, JComponent) -> Unit, revalidateButtons: () -> Unit)
|
||||
|
||||
class FileSource(fsItems: Set<PsiFileSystemItem>) : K2MoveSourceModel<PsiFileSystemItem> {
|
||||
init {
|
||||
observableUiSettings.registerK2SourceModelSettings(this)
|
||||
}
|
||||
|
||||
class FileSource(
|
||||
fsItems: Set<PsiFileSystemItem>,
|
||||
observableUiSettings: ObservableUiSettings,
|
||||
) : K2MoveSourceModel<PsiFileSystemItem>(observableUiSettings) {
|
||||
override var elements: Set<PsiFileSystemItem> = fsItems
|
||||
override val mppDeclarationsSelectedObservable: ObservableBooleanProperty = ConstantBooleanObservableProperty(false)
|
||||
|
||||
override fun toDescriptor(): K2MoveSourceDescriptor.FileSource = K2MoveSourceDescriptor.FileSource(elements)
|
||||
|
||||
@@ -47,9 +61,8 @@ sealed interface K2MoveSourceModel<T : PsiElement> {
|
||||
}
|
||||
|
||||
panel.group(RefactoringBundle.message("move.files.group")) {
|
||||
lateinit var list: JBList<TargetPresentation>
|
||||
row {
|
||||
list = cell(JBList(CollectionListModel(presentableFiles)).apply {
|
||||
cell(JBList(CollectionListModel(presentableFiles)).apply {
|
||||
cellRenderer = createTargetPresentationRenderer { it }
|
||||
}).align(Align.FILL).component
|
||||
}.resizableRow()
|
||||
@@ -57,9 +70,14 @@ sealed interface K2MoveSourceModel<T : PsiElement> {
|
||||
}
|
||||
}
|
||||
|
||||
class ElementSource(declarations: Set<KtNamedDeclaration>) : K2MoveSourceModel<KtNamedDeclaration> {
|
||||
class ElementSource(
|
||||
declarations: Set<KtNamedDeclaration>,
|
||||
observableUiSettings: ObservableUiSettings,
|
||||
) : K2MoveSourceModel<KtNamedDeclaration>(observableUiSettings) {
|
||||
override var elements: Set<KtNamedDeclaration> = declarations
|
||||
private set
|
||||
override val mppDeclarationsSelectedObservable: MutableBooleanProperty =
|
||||
AtomicBooleanProperty(hasExpectOrActualElements())
|
||||
|
||||
private lateinit var memberSelectionPanel: KotlinMemberSelectionPanel
|
||||
|
||||
@@ -83,6 +101,7 @@ sealed interface K2MoveSourceModel<T : PsiElement> {
|
||||
val table = memberSelectionPanel.table
|
||||
table.addMemberInfoChangeListener {
|
||||
elements = table.selectedMemberInfos.map { it.member }.toSet()
|
||||
mppDeclarationsSelectedObservable.set(hasExpectOrActualElements())
|
||||
if (elements.isEmpty()) {
|
||||
onError(KotlinBundle.message("text.no.elements.to.move.are.selected"), memberSelectionPanel.table)
|
||||
} else {
|
||||
@@ -116,6 +135,9 @@ sealed interface K2MoveSourceModel<T : PsiElement> {
|
||||
isChecked = elementsToMove.contains(declaration)
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasExpectOrActualElements(): Boolean =
|
||||
elements.any { it.isExpectOrActual() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+117
-45
@@ -6,9 +6,11 @@ import com.intellij.ide.util.DirectoryChooser
|
||||
import com.intellij.ide.util.TreeJavaClassChooserDialog
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.application.ReadAction
|
||||
import com.intellij.openapi.observable.properties.AtomicBooleanProperty
|
||||
import com.intellij.openapi.observable.properties.MutableBooleanProperty
|
||||
import com.intellij.openapi.observable.properties.PropertyGraph
|
||||
import com.intellij.openapi.observable.properties.*
|
||||
import com.intellij.openapi.observable.util.and
|
||||
import com.intellij.openapi.observable.util.equalsTo
|
||||
import com.intellij.openapi.observable.util.operation
|
||||
import com.intellij.openapi.observable.util.transform
|
||||
import com.intellij.openapi.roots.ProjectFileIndex
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton
|
||||
import com.intellij.openapi.util.registry.Registry
|
||||
@@ -33,37 +35,42 @@ import org.jetbrains.kotlin.idea.core.util.toPsiDirectory
|
||||
import org.jetbrains.kotlin.idea.k2.refactoring.move.descriptor.K2MoveTargetDescriptor
|
||||
import org.jetbrains.kotlin.idea.refactoring.ui.KotlinDestinationFolderComboBox
|
||||
import org.jetbrains.kotlin.idea.refactoring.ui.KotlinFileChooserDialog
|
||||
import org.jetbrains.kotlin.idea.util.sourceRoot
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.event.DocumentEvent
|
||||
|
||||
sealed interface K2MoveTargetModel {
|
||||
val directory: PsiDirectory
|
||||
sealed class K2MoveTargetModel(
|
||||
protected val observableUiSettings: ObservableUiSettings
|
||||
) : K2TargetModelObservableSettings {
|
||||
init {
|
||||
observableUiSettings.registerK2TargetModelSettings(this)
|
||||
}
|
||||
|
||||
val pkgName: FqName
|
||||
abstract val directory: PsiDirectory
|
||||
|
||||
val explicitPkgMoveFqName: FqName?
|
||||
abstract val pkgName: FqName
|
||||
|
||||
val observableUiSettings: ObservableUiSettings
|
||||
abstract val explicitPkgMoveFqName: FqName?
|
||||
|
||||
/**
|
||||
* Creates a [K2MoveTargetDescriptor] from this model.
|
||||
* @param kmpSourceRoot source KMP directory that corresponds to the base [directory] in a different source set.
|
||||
* `null` for non expect-actual moves.
|
||||
*/
|
||||
fun toDescriptor(kmpSourceRoot: PsiDirectory? = null): K2MoveTargetDescriptor
|
||||
abstract fun toDescriptor(kmpSourceRoot: PsiDirectory? = null): K2MoveTargetDescriptor
|
||||
|
||||
fun buildPanel(panel: Panel, onError: (String?, JComponent) -> Unit, revalidateButtons: () -> Unit)
|
||||
abstract fun buildPanel(panel: Panel, onError: (String?, JComponent) -> Unit, revalidateButtons: () -> Unit)
|
||||
|
||||
@ApiStatus.Internal
|
||||
abstract class SourceDirectoryChooser(
|
||||
override var pkgName: FqName,
|
||||
override var directory: PsiDirectory,
|
||||
override val explicitPkgMoveFqName: FqName?,
|
||||
override val observableUiSettings: ObservableUiSettings,
|
||||
) : K2MoveTargetModel {
|
||||
observableUiSettings: ObservableUiSettings,
|
||||
) : K2MoveTargetModel(observableUiSettings) {
|
||||
private val initialDirectory = directory
|
||||
|
||||
protected lateinit var pkgChooser: PackageNameReferenceEditorCombo
|
||||
@@ -163,20 +170,24 @@ sealed interface K2MoveTargetModel {
|
||||
override fun buildPanel(panel: Panel, onError: (String?, JComponent) -> Unit, revalidateButtons: () -> Unit) {
|
||||
panel.installPkgChooser(onError, revalidateButtons)
|
||||
}
|
||||
|
||||
override val sourceSetSuffix: ObservableProperty<String?>
|
||||
get() = ConstantObservableProperty(null)
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
abstract class FileChooser(
|
||||
fileName: String,
|
||||
@set:ApiStatus.Internal
|
||||
var fileName: String,
|
||||
pkg: FqName,
|
||||
directory: PsiDirectory,
|
||||
explicitPkgMoveFqName: FqName?,
|
||||
observableUiSettings: ObservableUiSettings,
|
||||
) : SourceDirectoryChooser(pkg, directory, explicitPkgMoveFqName, observableUiSettings) {
|
||||
var fileName: String = fileName
|
||||
protected set
|
||||
|
||||
private var selectedFile: KtFile? = null
|
||||
private val mppSuffixObservableProperty: AtomicProperty<String?> =
|
||||
AtomicProperty(findFileSuffix(fileName))
|
||||
|
||||
override fun updateDirectory(onError: (String?, JComponent) -> Unit, revalidateButtons: () -> Unit) {
|
||||
super.updateDirectory(onError, revalidateButtons)
|
||||
@@ -220,10 +231,21 @@ sealed interface K2MoveTargetModel {
|
||||
} else {
|
||||
onError(null, fileChooser)
|
||||
}
|
||||
mppSuffixObservableProperty.set(findFileSuffix(fileName))
|
||||
revalidateButtons()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override val sourceSetSuffix: ObservableProperty<String?>
|
||||
get() = mppSuffixObservableProperty
|
||||
|
||||
private fun findFileSuffix(fileName: String): String? {
|
||||
val sourceRootVirtualFile = directory.sourceRoot ?: return null
|
||||
val sourceRootPsiDir = sourceRootVirtualFile.toPsiDirectory(directory.project) ?: return null
|
||||
val suffix = findSourceSetNameStem(sourceRootPsiDir)
|
||||
return if (fileName.endsWith(".$suffix.kt")) suffix else null
|
||||
}
|
||||
}
|
||||
|
||||
class File(
|
||||
@@ -274,7 +296,8 @@ sealed interface K2MoveTargetModel {
|
||||
return if (destinationTargetType == MoveTargetType.CLASS && selectedClass != null) {
|
||||
K2MoveTargetDescriptor.ClassOrObject(selectedClass)
|
||||
} else {
|
||||
K2MoveTargetDescriptor.File(fileName, pkgName, kmpSourceRoot ?: directory)
|
||||
val fileNameWithKmpSuffixHandled = findSuffixedFileName(fileName, kmpSourceRoot)
|
||||
K2MoveTargetDescriptor.File(fileNameWithKmpSuffixHandled, pkgName, kmpSourceRoot ?: directory)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,43 +345,92 @@ sealed interface K2MoveTargetModel {
|
||||
})
|
||||
}
|
||||
|
||||
private fun Panel.installTargetFileSelector(
|
||||
onError: (String?, JComponent) -> Unit,
|
||||
revalidateButtons: () -> Unit,
|
||||
installTitleElement: Row.() -> Unit,
|
||||
) {
|
||||
val iconInfoObservableProperty = observableUiSettings.mppDeclarationsSettingObservable
|
||||
.and(observableUiSettings.sourceSetSuffix.transform { it.isNullOrBlank() })
|
||||
val iconWarningObservableProperty = observableUiSettings.mppDeclarationsSettingObservable
|
||||
.and(observableUiSettings.sourceSetSuffix.transform { !it.isNullOrBlank() })
|
||||
|
||||
row {
|
||||
panel {
|
||||
row {
|
||||
installTitleElement()
|
||||
icon(AllIcons.General.ContextHelp).align(AlignX.RIGHT).gap(RightGap.SMALL).applyToComponent {
|
||||
toolTipText = RefactoringBundle.message("tooltip.text.move.kmp.target.file")
|
||||
}.visibleIf(observableUiSettings.mppDeclarationsSettingObservable)
|
||||
}
|
||||
}
|
||||
installFileChooser(onError, revalidateButtons)
|
||||
}.layout(RowLayout.LABEL_ALIGNED)
|
||||
row("") {
|
||||
icon(AllIcons.General.Warning).align(AlignX.LEFT).gap(RightGap.SMALL)
|
||||
.visibleIf(iconWarningObservableProperty)
|
||||
icon(AllIcons.General.Information).align(AlignX.LEFT).gap(RightGap.SMALL)
|
||||
.visibleIf(iconInfoObservableProperty)
|
||||
val warningLabel = label("").bindText(
|
||||
operation(iconInfoObservableProperty, iconWarningObservableProperty) { info, warning ->
|
||||
when {
|
||||
info -> RefactoringBundle.message("move.added.kmp.suffixes.info")
|
||||
warning -> RefactoringBundle.message(
|
||||
"move.added.kmp.suffixes.warning.0",
|
||||
observableUiSettings.sourceSetSuffix.get().orEmpty(),
|
||||
)
|
||||
else -> ""
|
||||
}
|
||||
}
|
||||
).visibleIf(observableUiSettings.mppDeclarationsSettingObservable)
|
||||
// placeholder to avoid resizing on changes in the settings
|
||||
label("").visibleIf(observableUiSettings.mppDeclarationsSettingObservable.equalsTo(false))
|
||||
.applyToComponent { preferredSize = warningLabel.component.preferredSize }
|
||||
}
|
||||
}
|
||||
|
||||
private fun Panel.installTargetSelectionRadioGroup(
|
||||
onError: (String?, JComponent) -> Unit,
|
||||
revalidateButtons: () -> Unit,
|
||||
) {
|
||||
buttonsGroup(indent = false) {
|
||||
installTargetFileSelector(onError, revalidateButtons) {
|
||||
radioButton(KotlinBundle.message("refactoring.file.destination"), MoveTargetType.FILE)
|
||||
.onChanged {
|
||||
destinationTargetType = MoveTargetType.FILE
|
||||
fileChooser.isEnabled = true
|
||||
pkgChooser.isEnabled = true
|
||||
destinationChooser.isEnabled = true
|
||||
classChooser.isEnabled = false
|
||||
revalidateButtons()
|
||||
}
|
||||
}
|
||||
row {
|
||||
radioButton(KotlinBundle.message("refactoring.class.destination"), MoveTargetType.CLASS)
|
||||
.onChanged {
|
||||
destinationTargetType = MoveTargetType.CLASS
|
||||
fileChooser.isEnabled = false
|
||||
pkgChooser.isEnabled = false
|
||||
destinationChooser.isEnabled = false
|
||||
classChooser.isEnabled = true
|
||||
revalidateButtons()
|
||||
}
|
||||
installClassTargetChooser(onError, revalidateButtons)
|
||||
}.layout(RowLayout.LABEL_ALIGNED)
|
||||
}.bind(::destinationTargetType.toMutableProperty())
|
||||
}
|
||||
|
||||
override fun buildPanel(
|
||||
panel: Panel,
|
||||
onError: (String?, JComponent) -> Unit,
|
||||
revalidateButtons: () -> Unit
|
||||
) {
|
||||
panel.installPkgChooser(onError, revalidateButtons)
|
||||
|
||||
if (Registry.`is`("kotlin.move.show.move.to.class")) {
|
||||
panel.buttonsGroup(indent = true) {
|
||||
panel.row {
|
||||
radioButton(KotlinBundle.message("refactoring.file.destination"), MoveTargetType.FILE)
|
||||
.onChanged {
|
||||
destinationTargetType = MoveTargetType.FILE
|
||||
fileChooser.isEnabled = true
|
||||
pkgChooser.isEnabled = true
|
||||
destinationChooser.isEnabled = true
|
||||
classChooser.isEnabled = false
|
||||
revalidateButtons()
|
||||
}
|
||||
installFileChooser(onError, revalidateButtons)
|
||||
}
|
||||
panel.row {
|
||||
radioButton(KotlinBundle.message("refactoring.class.destination"), MoveTargetType.CLASS)
|
||||
.onChanged {
|
||||
destinationTargetType = MoveTargetType.CLASS
|
||||
fileChooser.isEnabled = false
|
||||
pkgChooser.isEnabled = false
|
||||
destinationChooser.isEnabled = false
|
||||
classChooser.isEnabled = true
|
||||
revalidateButtons()
|
||||
}
|
||||
installClassTargetChooser(onError, revalidateButtons)
|
||||
}
|
||||
}.bind(::destinationTargetType.toMutableProperty())
|
||||
panel.installTargetSelectionRadioGroup(onError, revalidateButtons)
|
||||
} else {
|
||||
panel.row {
|
||||
installFileChooser(onError, revalidateButtons)
|
||||
panel.installTargetFileSelector(onError, revalidateButtons) {
|
||||
label(KotlinBundle.message("label.text.file"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+40
-14
@@ -2,32 +2,58 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.k2.refactoring.move.ui
|
||||
|
||||
import com.intellij.openapi.observable.properties.ObservableBooleanProperty
|
||||
import com.intellij.openapi.observable.properties.ObservableProperty
|
||||
|
||||
interface ObservableUiSettings : K2MoveModelObservableSettings {
|
||||
interface ObservableUiSettings : K2MoveModelObservableSettings, K2SourceModelObservableSettings, K2TargetModelObservableSettings {
|
||||
fun registerK2MoveModelSettings(settings: K2MoveModelObservableSettings)
|
||||
fun registerK2SourceModelSettings(settings: K2SourceModelObservableSettings)
|
||||
fun registerK2TargetModelSettings(settings: K2TargetModelObservableSettings)
|
||||
}
|
||||
|
||||
interface K2MoveModelObservableSettings {
|
||||
val searchReferencesObservable: ObservableBooleanProperty
|
||||
val searchForTextObservable: ObservableBooleanProperty
|
||||
val searchInCommentsObservable: ObservableBooleanProperty
|
||||
val mppDeclarationsObservable: ObservableBooleanProperty
|
||||
val searchReferencesSettingObservable: ObservableProperty<Boolean>
|
||||
val searchForTextSettingObservable: ObservableProperty<Boolean>
|
||||
val searchInCommentsSettingObservable: ObservableProperty<Boolean>
|
||||
val mppDeclarationsSettingObservable: ObservableProperty<Boolean>
|
||||
}
|
||||
|
||||
interface K2SourceModelObservableSettings {
|
||||
val mppDeclarationsSelectedObservable: ObservableProperty<Boolean>
|
||||
}
|
||||
|
||||
interface K2TargetModelObservableSettings {
|
||||
val sourceSetSuffix: ObservableProperty<String?>
|
||||
}
|
||||
|
||||
internal class ObservableUiSettingsImpl : ObservableUiSettings {
|
||||
private lateinit var k2MoveModelSettings: K2MoveModelObservableSettings
|
||||
private lateinit var k2TargetModelSettings: K2TargetModelObservableSettings
|
||||
private lateinit var k2SourceModelSettings: K2SourceModelObservableSettings
|
||||
|
||||
override val searchReferencesObservable: ObservableBooleanProperty
|
||||
get() = k2MoveModelSettings.searchReferencesObservable
|
||||
override val searchForTextObservable: ObservableBooleanProperty
|
||||
get() = k2MoveModelSettings.searchForTextObservable
|
||||
override val searchInCommentsObservable: ObservableBooleanProperty
|
||||
get() = k2MoveModelSettings.searchInCommentsObservable
|
||||
override val mppDeclarationsObservable: ObservableBooleanProperty
|
||||
get() = k2MoveModelSettings.mppDeclarationsObservable
|
||||
override val searchReferencesSettingObservable: ObservableProperty<Boolean>
|
||||
get() = k2MoveModelSettings.searchReferencesSettingObservable
|
||||
override val searchForTextSettingObservable: ObservableProperty<Boolean>
|
||||
get() = k2MoveModelSettings.searchForTextSettingObservable
|
||||
override val searchInCommentsSettingObservable: ObservableProperty<Boolean>
|
||||
get() = k2MoveModelSettings.searchInCommentsSettingObservable
|
||||
override val mppDeclarationsSettingObservable: ObservableProperty<Boolean>
|
||||
get() = k2MoveModelSettings.mppDeclarationsSettingObservable
|
||||
|
||||
override val mppDeclarationsSelectedObservable: ObservableProperty<Boolean>
|
||||
get() = k2SourceModelSettings.mppDeclarationsSelectedObservable
|
||||
|
||||
override val sourceSetSuffix: ObservableProperty<String?>
|
||||
get() = k2TargetModelSettings.sourceSetSuffix
|
||||
|
||||
override fun registerK2MoveModelSettings(settings: K2MoveModelObservableSettings) {
|
||||
k2MoveModelSettings = settings
|
||||
}
|
||||
|
||||
override fun registerK2SourceModelSettings(settings: K2SourceModelObservableSettings) {
|
||||
k2SourceModelSettings = settings
|
||||
}
|
||||
|
||||
override fun registerK2TargetModelSettings(settings: K2TargetModelObservableSettings) {
|
||||
k2TargetModelSettings = settings
|
||||
}
|
||||
}
|
||||
|
||||
+31
@@ -7,13 +7,18 @@ import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFileSystemItem
|
||||
import com.intellij.psi.util.parentOfType
|
||||
import org.jetbrains.kotlin.analysis.api.projectStructure.KaModuleProvider
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.idea.base.externalSystem.KotlinBuildSystemFacade
|
||||
import org.jetbrains.kotlin.idea.base.util.module
|
||||
import org.jetbrains.kotlin.idea.core.getImplicitPackagePrefix
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtNamedDeclaration
|
||||
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isExpectDeclaration
|
||||
|
||||
internal fun String.isValidKotlinFile(): Boolean {
|
||||
return endsWith(KotlinLanguage.INSTANCE.associatedFileType?.defaultExtension ?: return false) || endsWith(".kts")
|
||||
@@ -65,3 +70,29 @@ internal fun findExplicitPkgMoveFqName(elementsToMove: List<PsiElement>): FqName
|
||||
|
||||
internal fun K2MoveTargetModel.isMoveToExplicitPackage(): Boolean =
|
||||
pkgName == explicitPkgMoveFqName
|
||||
|
||||
internal fun KtNamedDeclaration.isExpectOrActual(): Boolean =
|
||||
isExpectDeclaration() || hasActualModifier()
|
||||
|
||||
internal fun findSourceSetNameStem(kmpSourceRoot: PsiDirectory): String? {
|
||||
val project = kmpSourceRoot.project
|
||||
val kaModule = KaModuleProvider.getModule(project, kmpSourceRoot, useSiteModule = null)
|
||||
val dependsOnDependencies = kaModule.directDependsOnDependencies
|
||||
if (dependsOnDependencies.isEmpty()) return null
|
||||
val workspaceModule = kmpSourceRoot.module ?: return null
|
||||
val sourceSet = KotlinBuildSystemFacade.getInstance().findSourceSet(workspaceModule)
|
||||
val fullSourceSetName = sourceSet?.name ?: workspaceModule.name.split(".").last()
|
||||
val sourceSetNameStem = kmpSourceSetDefaultSuffixes.firstOrNull { fullSourceSetName.endsWith(it) }
|
||||
?.let { sourceSetNameSuffix -> fullSourceSetName.removeSuffix(sourceSetNameSuffix) }
|
||||
?: fullSourceSetName
|
||||
return sourceSetNameStem
|
||||
}
|
||||
|
||||
internal fun findSuffixedFileName(baseFileName: String, kmpSourceRoot: PsiDirectory?): String {
|
||||
if (kmpSourceRoot == null) return baseFileName
|
||||
val sourceSetSuffix = findSourceSetNameStem(kmpSourceRoot) ?: return baseFileName
|
||||
val baseWithoutKt = baseFileName.substringBeforeLast(".kt")
|
||||
return "$baseWithoutKt.$sourceSetSuffix.kt"
|
||||
}
|
||||
|
||||
private val kmpSourceSetDefaultSuffixes = listOf("Main", "Test")
|
||||
|
||||
+2
-2
@@ -41,8 +41,8 @@ abstract class AbstractK2CheckDescriptorMultiModuleMoveTest : KotlinMultiFileTes
|
||||
config: JsonObject
|
||||
) {
|
||||
allowAnalysisOnEdt {
|
||||
val elementAtCaret = elementsAtCaret.single()
|
||||
val editor = elementAtCaret.findExistingEditor()
|
||||
val firstElementAtCaret = elementsAtCaret.first()
|
||||
val editor = firstElementAtCaret.findExistingEditor()
|
||||
val moveModel = K2MoveModel.create(elementsAtCaret.toTypedArray<PsiElement>(), null, editor)
|
||||
?: error("Failed to create move model")
|
||||
configureMoveModel(moveModel)
|
||||
|
||||
+30
-5
@@ -54,6 +54,20 @@ class K2CheckDescriptorMultiModuleMoveTest : AbstractK2CheckDescriptorMultiModul
|
||||
)
|
||||
}
|
||||
|
||||
@TestMetadata("moveActualAndRegularFunctionConventionalSourceSetNames")
|
||||
@Throws(Exception::class)
|
||||
fun testMoveActualAndRegularFunctionConventionalSourceSetNames() {
|
||||
doTest(
|
||||
"moveActualAndRegularFunctionConventionalSourceSetNames/moveActualAndRegularFunctionConventionalSourceSetNames.test",
|
||||
configureMoveModel = { moveModel ->
|
||||
setAllMoveSettingsOn(moveModel)
|
||||
setNewTargetPackageInSameRoot(moveModel, FqName("bar"))
|
||||
setTargetFile(moveModel, "target.kt")
|
||||
},
|
||||
checkMoveDescriptor = { moveOperationDescriptor -> checkNoMoveOutsideSourceRoot(moveOperationDescriptor) },
|
||||
)
|
||||
}
|
||||
|
||||
@TestMetadata("moveActualClass")
|
||||
@Throws(Exception::class)
|
||||
fun testMoveActualClass() {
|
||||
@@ -112,17 +126,28 @@ class K2CheckDescriptorMultiModuleMoveTest : AbstractK2CheckDescriptorMultiModul
|
||||
targetModel.directory = packageSubdirectory
|
||||
}
|
||||
|
||||
/**
|
||||
* Imitates target file change through the file chooser.
|
||||
*/
|
||||
private fun setTargetFile(moveModel: K2MoveModel, fileName: String) {
|
||||
val moveTarget = moveModel.target
|
||||
if (moveTarget !is K2MoveTargetModel.FileChooser)
|
||||
throw AssertionError("Unexpected move target model: ${moveTarget::class.simpleName}")
|
||||
moveTarget.fileName = fileName
|
||||
}
|
||||
|
||||
private fun checkNoMoveOutsideSourceRoot(moveOperationDescriptor: K2MoveOperationDescriptor<*>) {
|
||||
moveOperationDescriptor.moveDescriptors.forEach { descriptor ->
|
||||
val element = descriptor.source.elements.singleOrNull() ?: error("Single source element to move is expected")
|
||||
val elementSourceDir = element.containingFile.sourceRoot ?: error("Can't find source root for element")
|
||||
val targetRoot = descriptor.target.baseDirectory.sourceRoot ?: error("Can't find source root for target")
|
||||
assert(elementSourceDir == targetRoot) {
|
||||
"""Element was unexpectedly moved outside of its source root:
|
||||
descriptor.source.elements.forEach { element ->
|
||||
val elementSourceDir = element.containingFile.sourceRoot ?: error("Can't find source root for element")
|
||||
val targetRoot = descriptor.target.baseDirectory.sourceRoot ?: error("Can't find source root for target")
|
||||
assert(elementSourceDir == targetRoot) {
|
||||
"""Element was unexpectedly moved outside of its source root:
|
||||
|${element.text}
|
||||
|Element root: ${elementSourceDir.path}
|
||||
|Target root: ${targetRoot.path}
|
||||
|""".trimMargin()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user