KTIJ-10606 [kotlin] Move function to class: offload validation to background thread

Validator expects the result synchronously.
Checking class presence in it can lead to freezes.
The fix moves the class search before launching the validation,
which makes it possible to run the search read action on a pooled thread instead of EDT.

(cherry picked from commit 452eb04f1f30a7adbc012146d58881351b7c75c0)

GitOrigin-RevId: 7d41d2ff6bbd9b413551425c983dadab83278f43
This commit is contained in:
Pavel Kirpichenkov
2026-08-03 12:51:12 +00:00
committed by intellij-monorepo-bot
parent 5a90317a57
commit e5b66d2409
@@ -2,6 +2,7 @@
package org.jetbrains.kotlin.idea.k2.refactoring.move.ui
import com.intellij.ide.util.TreeJavaClassChooserDialog
import com.intellij.openapi.application.ModalityState
import com.intellij.openapi.application.ReadAction
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.observable.properties.AtomicBooleanProperty
@@ -26,6 +27,7 @@ import com.intellij.ui.dsl.builder.RowLayout
import com.intellij.ui.dsl.builder.panel
import com.intellij.ui.dsl.builder.toMutableProperty
import com.intellij.util.Alarm
import com.intellij.util.concurrency.AppExecutorUtil
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import org.jetbrains.annotations.ApiStatus
@@ -117,18 +119,17 @@ class K2MoveToClassDialog(
text = candidateList.selectedValue?.targetClassFqName?.asString().orEmpty()
}
private var isTargetClassFound = true
/**
* Installs a validator for the target class text field.
* The validator checks that the inserted FQ name can be resolved to a [KtClassOrObject].
* The validation can happen not more often than once per [VALIDATION_DELAY_MILLIS] ms.
* The validation cannot happen more often than once per [VALIDATION_DELAY_MILLIS] ms.
*/
private fun TextFieldWithBrowseButton.installTargetClassValidator() {
ComponentValidator(disposable).withValidator {
val isValid = ReadAction.nonBlocking(Callable {
currentTargetClass() != null
}).executeSynchronously()
okAction.isEnabled = isValid
if (!isValid) {
okAction.isEnabled = isTargetClassFound
if (!isTargetClassFound && classChooser.text.isNotEmpty()) {
ValidationInfo(KotlinBundle.message("refactoring.cannot.find.target.class"), textField)
} else null
}.installOn(textField)
@@ -137,7 +138,14 @@ class K2MoveToClassDialog(
override fun textChanged(e: DocumentEvent) {
validationAlarm.cancelAllRequests()
validationAlarm.addRequest({
ComponentValidator.getInstance(textField).ifPresent { validator -> validator.revalidate() }
ComponentValidator.getInstance(textField).ifPresent { validator ->
ReadAction.nonBlocking(Callable {
currentTargetClass() != null
}).finishOnUiThread(ModalityState.stateForComponent(this@installTargetClassValidator)) {
isTargetClassFound = it
validator.revalidate()
}.submit(AppExecutorUtil.getAppExecutorService())
}
}, delayMillis = VALIDATION_DELAY_MILLIS)
}
})