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 6de5517d3..2bfb7e311 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 000000000..6aea23958 --- /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.ui.targetsToolWindow.UTBotTargetsController +import org.utbot.cpp.clion.plugin.utils.activeProject + +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 9809334ec..32b4e8b85 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 org.utbot.cpp.clion.plugin.client.channels.GTestLogChannelImpl @@ -22,11 +23,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 3385c5836..3462855fe 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 @@ -8,11 +8,20 @@ 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) override suspend fun TestsGenServiceGrpcKt.TestsGenServiceCoroutineStub.send(cancellationJob: Job?): Testgen.ProjectTargetsResponse = diff --git a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/requests/test/BaseTestsRequest.kt b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/requests/test/BaseTestsRequest.kt index 2720a7be5..4d11e570a 100644 --- a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/requests/test/BaseTestsRequest.kt +++ b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/client/requests/test/BaseTestsRequest.kt @@ -8,6 +8,7 @@ import org.utbot.cpp.clion.plugin.client.handlers.TestsStreamHandler import org.utbot.cpp.clion.plugin.client.requests.BaseRequest import org.utbot.cpp.clion.plugin.utils.getLongestCommonPathFromRoot import org.utbot.cpp.clion.plugin.utils.isHeaderFile +import org.utbot.cpp.clion.plugin.utils.isSarifReport import org.utbot.cpp.clion.plugin.utils.logger import org.utbot.cpp.clion.plugin.utils.notifyInfo import testsgen.Testgen @@ -35,7 +36,7 @@ abstract class BaseTestsRequest(request: R, project: Project, private val pro } open fun getFocusTarget(generatedTestFiles: List): Path? { - return generatedTestFiles.filter { !isHeaderFile(it.fileName.toString()) }.getLongestCommonPathFromRoot() + return generatedTestFiles.filter { !isHeaderFile(it) && !isSarifReport(it) }.getLongestCommonPathFromRoot() } override fun logRequest() { diff --git a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/settings/UTBotAllProjectSettings.kt b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/settings/UTBotAllProjectSettings.kt index b0b9692bb..240f6288f 100644 --- a/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/settings/UTBotAllProjectSettings.kt +++ b/clion-plugin/src/main/kotlin/org/utbot/cpp/clion/plugin/settings/UTBotAllProjectSettings.kt @@ -20,8 +20,14 @@ class UTBotAllProjectSettings(val project: Project) { get() = project.service().state var projectPath: String - get() = storedSettings.projectPath ?: project.guessProjectDir()?.path - ?: error("Could not guess project path! Should be specified in settings by user") + get() { + // if true then there is nothing persisted in xml files and plugin was launched for the first time + if (storedSettings.projectPath == null) + // so we should guess the project path + storedSettings.projectPath = project.guessProjectDir()?.path + return storedSettings.projectPath + ?: error("Could not guess project path! Should be specified in settings by user") + } set(value) { storedSettings.projectPath = value } @@ -49,7 +55,8 @@ class UTBotAllProjectSettings(val project: Project) { */ val isRemoteScenario: Boolean get() { - val isLocalHost = projectIndependentSettings.serverName == "localhost" || projectIndependentSettings.serverName == "127.0.0.01" + val isLocalHost = + projectIndependentSettings.serverName == "localhost" || projectIndependentSettings.serverName == "127.0.0.01" return !(storedSettings.remotePath == projectPath && isLocalHost) || isWindows } 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 2cd593efc..84942af07 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 get() = 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,8 +84,6 @@ class UTBotTargetsController(val project: Project) { } } - fun createTargetsToolWindow(): UTBotTargetsToolWindow = UTBotTargetsToolWindow(listModel, this) - fun selectionChanged(selectedTarget: UTBotTarget) { // when user selects target update model settings.storedSettings.targetPath = selectedTarget.path @@ -94,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 f2f05ff9d..7c251afe8 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, - private 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 a00b7f62a..380b69357 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 @@ -11,9 +12,9 @@ class UTBotTargetsToolWindowFactory : ToolWindowFactory { override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) { logger.info("createToolWindowContent was called") val contentManager = toolWindow.contentManager - val utbotToolWindow = UTBotTargetsController(project).createTargetsToolWindow() - - val content = contentManager.factory.createContent(utbotToolWindow, null, false,) - contentManager.addContent(content) + val content = contentManager.factory.createContent( + 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 47440f4a8..10e9a71e5 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) : AbstractWizardLearn 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