From d71f1a2c23fcb3b9f311152f4893bebed2d90a1b Mon Sep 17 00:00:00 2001 From: Arseniy Volynets Date: Sun, 31 Jul 2022 19:43:07 +0300 Subject: [PATCH] Add refresh action, show busy icon on refresh --- .../cpp/clion/plugin/actions/FocusAction.kt | 6 +-- .../plugin/actions/RefreshTargetsAction.kt | 19 ++++++++ .../cpp/clion/plugin/client/ClientManager.kt | 5 +- .../client/requests/ProjectTargetsRequest.kt | 14 +++++- .../UTBotProjectIndependentSettings.kt | 8 ++++ .../UTBotTargetsController.kt | 46 ++++++++++++------- .../UTBotTargetsToolWindow.kt | 37 +++++++++++++-- .../UTBotTargetsToolWindowFactory.kt | 3 +- .../cpp/clion/plugin/ui/wizard/UTBotWizard.kt | 1 + .../utbot/cpp/clion/plugin/utils/FileUtils.kt | 3 +- .../main/resources/messages/UTBot.properties | 1 + 11 files changed, 113 insertions(+), 30 deletions(-) create mode 100644 clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/actions/RefreshTargetsAction.kt diff --git a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/actions/FocusAction.kt b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/actions/FocusAction.kt index 6de5517d..2bfb7e31 100644 --- a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/actions/FocusAction.kt +++ b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/actions/FocusAction.kt @@ -14,13 +14,13 @@ class FocusAction(val path: Path) : AnAction("Show") { ?: error("Focus action should be disabled for path $path") val project = e.activeProject() - val projectInstance = PsiManager.getInstance(project) + val psiManager = PsiManager.getInstance(project) if (virtualFile.isDirectory) { - projectInstance.findDirectory(virtualFile)?.navigate(true) + psiManager.findDirectory(virtualFile)?.navigate(true) } else { - projectInstance.findFile(virtualFile)?.navigate(true) + psiManager.findFile(virtualFile)?.navigate(true) } } diff --git a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/actions/RefreshTargetsAction.kt b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/actions/RefreshTargetsAction.kt new file mode 100644 index 00000000..89378413 --- /dev/null +++ b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/actions/RefreshTargetsAction.kt @@ -0,0 +1,19 @@ +package org.utbot.cpp.clion.plugin.actions + +import com.intellij.icons.AllIcons +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.components.service +import org.utbot.cpp.clion.plugin.grpc.activeProject +import org.utbot.cpp.clion.plugin.ui.targetsToolWindow.UTBotTargetsController + +class RefreshTargetsAction: AnAction() { + override fun actionPerformed(e: AnActionEvent) { + e.activeProject().service().requestTargetsFromServer() + } + + override fun update(e: AnActionEvent) { + e.presentation.isEnabledAndVisible = e.project != null + e.presentation.icon = AllIcons.Actions.Refresh + } +} \ No newline at end of file diff --git a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/ClientManager.kt b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/ClientManager.kt index 534671f8..239113e5 100644 --- a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/ClientManager.kt +++ b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/ClientManager.kt @@ -1,6 +1,7 @@ package org.utbot.cpp.clion.plugin.client import com.intellij.openapi.Disposable +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.Service import com.intellij.openapi.project.Project import kotlin.random.Random @@ -19,11 +20,11 @@ class ClientManager(val project: Project): Disposable { } private fun subscribeToEvents() { - with(project.messageBus.connect()) { + with(ApplicationManager.getApplication().messageBus.connect()) { subscribe(ConnectionSettingsListener.TOPIC, object : ConnectionSettingsListener { override fun connectionSettingsChanged(newPort: Int, newServerName: String) { if (newPort != client.port || newServerName != client.serverName) { - project.logger.trace{ "Connection settings changed. Setting up new client." } + project.logger.trace { "Connection settings changed. Setting up new client." } client.dispose() client = Client(project, clientId, loggingChannels) } diff --git a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/requests/ProjectTargetsRequest.kt b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/requests/ProjectTargetsRequest.kt index 5da7db87..f4d1adff 100644 --- a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/requests/ProjectTargetsRequest.kt +++ b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/requests/ProjectTargetsRequest.kt @@ -2,15 +2,27 @@ package org.utbot.cpp.clion.plugin.client.requests import com.intellij.openapi.project.Project import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import org.utbot.cpp.clion.plugin.utils.logger import testsgen.Testgen import testsgen.TestsGenServiceGrpcKt class ProjectTargetsRequest( project: Project, request: Testgen.ProjectTargetsRequest, - val processTargets: suspend (Testgen.ProjectTargetsResponse)->Unit + val processTargets: suspend (Testgen.ProjectTargetsResponse)->Unit, + val onError: suspend (Throwable) -> Unit ): BaseRequest(request, project) { override val logMessage: String = "Sending request to get PROJECT TARGETS." + + override suspend fun execute(stub: TestsGenServiceGrpcKt.TestsGenServiceCoroutineStub, cancellationJob: Job?) { + try { + super.execute(stub, cancellationJob) + } catch (e: Throwable) { + onError(e) + } + } + override suspend fun Testgen.ProjectTargetsResponse.handle(cancellationJob: Job?) { processTargets(this) } diff --git a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/settings/UTBotProjectIndependentSettings.kt b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/settings/UTBotProjectIndependentSettings.kt index f82751f1..9b47be93 100644 --- a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/settings/UTBotProjectIndependentSettings.kt +++ b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/settings/UTBotProjectIndependentSettings.kt @@ -1,9 +1,11 @@ package org.utbot.cpp.clion.plugin.settings +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.components.PersistentStateComponent import com.intellij.openapi.components.Service import com.intellij.openapi.components.State import com.intellij.openapi.components.Storage +import org.utbot.cpp.clion.plugin.listeners.ConnectionSettingsListener /** * Settings that are the same for all projects @@ -22,6 +24,11 @@ class UTBotProjectIndependentSettings : PersistentStateComponent()) private val logger = project.logger + val targetsToolWindow: UTBotTargetsToolWindow by lazy { UTBotTargetsToolWindow(listModel, this) } val targets: List get() = listModel.toList() @@ -32,23 +36,35 @@ class UTBotTargetsController(val project: Project) { fun requestTargetsFromServer() { val currentClient = project.getCurrentClient() + invokeOnEdt { + listModel.removeAll() + targetsToolWindow.setBusy(true) + } ProjectTargetsRequest( project, getProjectTargetsGrpcRequest(project), - ) { targetsResponse -> - invokeOnEdt { - listModel.apply { - val oldTargetList = toList() - oldTargetList.addAll( - targetsResponse.targetsList.map { projectTarget -> - UTBotTarget(projectTarget, project) - }) - listModel.replaceAll(oldTargetList.distinct()) + processTargets = { targetsResponse: Testgen.ProjectTargetsResponse -> + invokeOnEdt { + targetsToolWindow.setBusy(false) + + listModel.apply { + listModel.replaceAll( + targetsResponse.targetsList.map { projectTarget -> + UTBotTarget(projectTarget, project) + }) + } } - } - }.let { targetsRequest -> + }, + onError = { + invokeOnEdt { + targetsToolWindow.setBusy(false) + } + }).let { targetsRequest -> if (!currentClient.isServerAvailable()) { logger.error { "Could not request targets from server: server is unavailable!" } + invokeOnEdt { + targetsToolWindow.setBusy(false) + } return } logger.trace { "Requesting project targets from server!" } @@ -68,10 +84,6 @@ class UTBotTargetsController(val project: Project) { } } - fun createTargetsToolWindow(): UTBotTargetsToolWindow { - return UTBotTargetsToolWindow(listModel, this) - } - fun selectionChanged(selectedTarget: UTBotTarget) { // when user selects target update model settings.storedSettings.targetPath = selectedTarget.path @@ -96,7 +108,7 @@ class UTBotTargetsController(val project: Project) { UTBotEventsListener.CONNECTION_CHANGED_TOPIC, object : UTBotEventsListener { override fun onConnectionChange(oldStatus: ConnectionStatus, newStatus: ConnectionStatus) { - if (newStatus != oldStatus && newStatus == ConnectionStatus.CONNECTED) { + if (newStatus != oldStatus) { requestTargetsFromServer() } } diff --git a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/targetsToolWindow/UTBotTargetsToolWindow.kt b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/targetsToolWindow/UTBotTargetsToolWindow.kt index 4b69da5e..9a07d4c9 100644 --- a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/targetsToolWindow/UTBotTargetsToolWindow.kt +++ b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/targetsToolWindow/UTBotTargetsToolWindow.kt @@ -1,5 +1,11 @@ package org.utbot.cpp.clion.plugin.ui.targetsToolWindow +import com.intellij.openapi.actionSystem.ActionGroup +import com.intellij.openapi.actionSystem.ActionManager +import com.intellij.openapi.actionSystem.ActionPlaces +import com.intellij.openapi.actionSystem.ActionToolbar +import com.intellij.openapi.actionSystem.DefaultActionGroup +import com.intellij.openapi.project.DumbAware import com.intellij.openapi.ui.SimpleToolWindowPanel import com.intellij.ui.CollectionListModel import com.intellij.ui.ColoredListCellRenderer @@ -8,24 +14,47 @@ import com.intellij.ui.components.JBList import com.intellij.ui.components.JBScrollPane import javax.swing.JList import javax.swing.ListSelectionModel +import org.utbot.cpp.clion.plugin.UTBot +import org.utbot.cpp.clion.plugin.actions.RefreshTargetsAction +import org.utbot.cpp.clion.plugin.utils.logger class UTBotTargetsToolWindow( listModel: CollectionListModel, - val controller: UTBotTargetsController -): SimpleToolWindowPanel(true, true) { + private val controller: UTBotTargetsController +): SimpleToolWindowPanel(true, true), DumbAware { private val uiList = JBList(listModel) init { - val panel = JBScrollPane() + toolbar = createActionToolBar().let { + it.targetComponent = this.component + it.component + } + uiList.cellRenderer = Renderer() uiList.selectionMode = ListSelectionModel.SINGLE_SELECTION uiList.addListSelectionListener { - controller.selectionChanged(uiList.selectedValue) + if (!uiList.isSelectionEmpty) + controller.selectionChanged(uiList.selectedValue) } + uiList.setEmptyText(UTBot.message("targets.notargets.description")) + + val panel = JBScrollPane() panel.setViewportView(uiList) setContent(panel) } + fun setBusy(busy: Boolean) { + uiList.setPaintBusy(busy) + } + + private fun createActionToolBar(isHorizontal: Boolean = false): ActionToolbar { + return ActionManager.getInstance().createActionToolbar(ActionPlaces.TOOLBAR, createActionGroup(), isHorizontal) + } + + private fun createActionGroup(): ActionGroup { + return DefaultActionGroup(RefreshTargetsAction()) + } + private inner class Renderer : ColoredListCellRenderer() { override fun customizeCellRenderer( list: JList, diff --git a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/targetsToolWindow/UTBotTargetsToolWindowFactory.kt b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/targetsToolWindow/UTBotTargetsToolWindowFactory.kt index a15393bb..380b6935 100644 --- a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/targetsToolWindow/UTBotTargetsToolWindowFactory.kt +++ b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/targetsToolWindow/UTBotTargetsToolWindowFactory.kt @@ -1,5 +1,6 @@ package org.utbot.cpp.clion.plugin.ui.targetsToolWindow +import com.intellij.openapi.components.service import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import com.intellij.openapi.wm.ToolWindow @@ -12,7 +13,7 @@ class UTBotTargetsToolWindowFactory : ToolWindowFactory { logger.info("createToolWindowContent was called") val contentManager = toolWindow.contentManager val content = contentManager.factory.createContent( - UTBotTargetsController(project).createTargetsToolWindow(), null, false + project.service().targetsToolWindow, null, false ) toolWindow.contentManager.addContent(content) } diff --git a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/wizard/UTBotWizard.kt b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/wizard/UTBotWizard.kt index 62e31e6c..03689f85 100644 --- a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/wizard/UTBotWizard.kt +++ b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/ui/wizard/UTBotWizard.kt @@ -32,6 +32,7 @@ class UTBotWizard(private val project: Project) : AbstractWizard.getLongestCommonPathFromRoot(): Path? { fun refreshAndFindIOFile(filePath: String) = refreshAndFindIOFile(Paths.get(filePath)) fun createFileAndMakeDirs(filePath: Path, text: String) { - if (!Files.isRegularFile(filePath)) - error("File expected! But got: $filePath") with(filePath) { createFile() writeText(text) diff --git a/clion-plugin/src/main/resources/messages/UTBot.properties b/clion-plugin/src/main/resources/messages/UTBot.properties index b33ea98c..a9879551 100644 --- a/clion-plugin/src/main/resources/messages/UTBot.properties +++ b/clion-plugin/src/main/resources/messages/UTBot.properties @@ -52,3 +52,4 @@ advanced.timeoutPerFunction.title=Timeout per function: advanced.timeoutPerFunction.description=Maximum time (in seconds) alloted for generation tests per function. Set to non-positive number to disable it. Learn more advanced.timeoutPerTest.title=Timeout per test: advanced.timeoutPerTest.description=Maximum time (in seconds) alloted for a single test run. Set to non-positive number to disable it. Learn more +targets.notargets.description=No targets can be found by UTBot in current project