From e5b66d2409451e191bb979eb3512d09ab626ff54 Mon Sep 17 00:00:00 2001 From: Pavel Kirpichenkov Date: Fri, 24 Jul 2026 12:27:25 +0300 Subject: [PATCH] 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 --- .../move/ui/K2MoveToClassDialog.kt | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/move/ui/K2MoveToClassDialog.kt b/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/move/ui/K2MoveToClassDialog.kt index e2756ec17366..ec779046885e 100644 --- a/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/move/ui/K2MoveToClassDialog.kt +++ b/plugins/kotlin/refactorings/kotlin.refactorings.k2/src/org/jetbrains/kotlin/idea/k2/refactoring/move/ui/K2MoveToClassDialog.kt @@ -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) } })