Skip to content

Commit

Permalink
add ability to enable/disable plugin per project (#404)
Browse files Browse the repository at this point in the history
* fix merge conflicts

* add more docs

* don't hide toolwindows on disabling

* hide/show settings ui when enabling/disabling plugin

* required fixes

* rebase

* delete empty files

* set plugin disabled on default, show wizard on first enabled

* don't request targets in tests until ci is fixed

* fix console accessed not from edt using lazy delegate

* fix logging for tests using tinylog

* fix tests: enable plugin & wait for connection
  • Loading branch information
vol0n authored Oct 4, 2022
1 parent 10d73de commit 9314a3f
Show file tree
Hide file tree
Showing 63 changed files with 585 additions and 307 deletions.
2 changes: 2 additions & 0 deletions clion-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ dependencies {
implementation("com.google.protobuf:protobuf-kotlin:$protobufVersion")

implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2")
implementation("org.tinylog:tinylog-api-kotlin:2.5.0")
implementation("org.tinylog:tinylog-impl:2.5.0")

// testing with JUnit 5
testImplementation(platform("org.junit:junit-bom:5.8.2"))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,31 @@
package org.utbot.cpp.clion.plugin

import com.intellij.ide.util.RunOnceUtil
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.service
import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupActivity
import org.utbot.cpp.clion.plugin.client.ClientManager
import org.utbot.cpp.clion.plugin.client.ManagedClient
import org.utbot.cpp.clion.plugin.settings.settings
import org.utbot.cpp.clion.plugin.ui.wizard.UTBotWizard
import org.utbot.cpp.clion.plugin.utils.invokeOnEdt

class UTBotStartupActivity : StartupActivity {
override fun runActivity(project: Project) {
// We initialize Client here, so that initialization will not happen
// when user issues first generation request which would cause a UI freeze.
initializeClient(project)
guessPathsOnFirstOpen(project)
showWizardOnFirstOpen(project)
if (project.settings.storedSettings.isPluginEnabled) {
initializeClient(project)
guessPathsOnFirstOpen(project)
}
}

// Here we address the service ClientManager for the first time so that it
// will be initialized by the ide and Client will be created.
// Client in turn will create a grpc channel and start heart-beating the server.
private fun initializeClient(project: Project) = project.service<ClientManager>()

private fun initializeClient(project: Project) = project.service<ManagedClient>()

private fun showWizardOnFirstOpen(project: Project) {
if (!ApplicationManager.getApplication().isUnitTestMode) {
invokeOnEdt {
RunOnceUtil.runOnceForProject(project, "Show UTBot quick-start wizard to configure project") {
UTBotWizard(project).showAndGet()
}
}
}
}

private fun guessPathsOnFirstOpen(project: Project) {
RunOnceUtil.runOnceForProject(project, "Guess UTBot paths in settings") {
project.settings.predictPaths()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package org.utbot.cpp.clion.plugin.actions

import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import org.utbot.cpp.clion.plugin.UTBot
import org.utbot.cpp.clion.plugin.client.requests.CreateBuildDirRequest

class AskServerToGenerateBuildDir : AnAction(UTBot.message("projectConfigure.generate.buildDir")) {
class AskServerToGenerateBuildDir : UTBotBaseAction(UTBot.message("projectConfigure.generate.buildDir")) {

override fun actionPerformed(e: AnActionEvent) = CreateBuildDirRequest(e).executeUsingCurrentClient()
override fun actionPerformed(e: AnActionEvent) = CreateBuildDirRequest(e).execute()

override fun update(e: AnActionEvent) {
override fun updateIfEnabled(e: AnActionEvent) {
e.presentation.isEnabledAndVisible = e.project != null
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package org.utbot.cpp.clion.plugin.actions

import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import org.utbot.cpp.clion.plugin.UTBot
import org.utbot.cpp.clion.plugin.client.requests.GenerateJsonFilesRequest

class AskServerToGenerateJsonForProjectConfiguration : AnAction(UTBot.message("projectConfigure.generate.json")) {
class AskServerToGenerateJsonForProjectConfiguration : UTBotBaseAction(UTBot.message("projectConfigure.generate.json")) {

override fun actionPerformed(e: AnActionEvent) = GenerateJsonFilesRequest(e).executeUsingCurrentClient()
override fun actionPerformed(e: AnActionEvent) = GenerateJsonFilesRequest(e).execute()

override fun update(e: AnActionEvent) {
override fun updateIfEnabled(e: AnActionEvent) {
e.presentation.isEnabledAndVisible = e.project != null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,19 @@ package org.utbot.cpp.clion.plugin.actions
import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.ToggleAction
import com.intellij.openapi.project.Project
import org.utbot.cpp.clion.plugin.UTBot
import org.utbot.cpp.clion.plugin.settings.settings

class ChangeVerboseModeAction : ToggleAction() {
class ChangeVerboseModeAction : UTBotBaseToggleAction() {
override fun isSelected(e: AnActionEvent): Boolean {
updateActionText(e)
return e.getRequiredData(CommonDataKeys.PROJECT).settings.storedSettings.verbose
}

override fun isDumbAware(): Boolean = true

override fun update(e: AnActionEvent) {
super.update(e)
override fun updateIfEnabled(e: AnActionEvent) {
e.presentation.isEnabledAndVisible = (e.project != null)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package org.utbot.cpp.clion.plugin.actions

import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.psi.PsiManager
import org.utbot.cpp.clion.plugin.utils.activeProject
import java.nio.file.Path

class FocusAction(val path: Path) : AnAction("Show") {
class FocusAction(val path: Path) : UTBotBaseAction("Show") {

override fun actionPerformed(e: AnActionEvent) {
val virtualFile = LocalFileSystem.getInstance().findFileByNioFile(path)
Expand All @@ -24,7 +23,7 @@ class FocusAction(val path: Path) : AnAction("Show") {
}
}

override fun update(e: AnActionEvent) {
override fun updateIfEnabled(e: AnActionEvent) {
e.presentation.isEnabledAndVisible = LocalFileSystem.getInstance().findFileByNioFile(path) != null
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package org.utbot.cpp.clion.plugin.actions

import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.components.service
import org.utbot.cpp.clion.plugin.UTBot
import org.utbot.cpp.clion.plugin.client.ClientManager
import org.utbot.cpp.clion.plugin.client.ManagedClient

class ReconnectAction: AnAction(UTBot.message("actions.reconnect")) {
class ReconnectAction : UTBotBaseAction(UTBot.message("actions.reconnect")) {
override fun actionPerformed(e: AnActionEvent) {
e.project!!.service<ClientManager>().restartClient()
e.project!!.service<ManagedClient>().restartClient()
}

override fun update(e: AnActionEvent) {
override fun updateIfEnabled(e: AnActionEvent) {
e.presentation.isEnabledAndVisible = e.project != null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ import com.intellij.openapi.components.service
import org.utbot.cpp.clion.plugin.ui.utbotToolWindow.targetToolWindow.UTBotTargetsController
import org.utbot.cpp.clion.plugin.utils.activeProject

class RefreshTargetsAction: AnAction() {
class RefreshTargetsAction: UTBotBaseAction() {
override fun actionPerformed(e: AnActionEvent) {
e.activeProject().service<UTBotTargetsController>().requestTargetsFromServer()
}

override fun update(e: AnActionEvent) {
override fun updateIfEnabled(e: AnActionEvent) {
e.presentation.isEnabledAndVisible = e.project != null
e.presentation.icon = AllIcons.Actions.Refresh
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package org.utbot.cpp.clion.plugin.actions

import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import org.utbot.cpp.clion.plugin.UTBot
import org.utbot.cpp.clion.plugin.ui.wizard.UTBotWizard
import org.utbot.cpp.clion.plugin.utils.activeProject

class ShowWizardAction: AnAction(UTBot.message("wizard.show")) {
class ShowWizardAction: UTBotBaseAction(UTBot.message("wizard.show")) {

override fun actionPerformed(e: AnActionEvent) {
UTBotWizard(e.activeProject()).showAndGet()
}

override fun update(e: AnActionEvent) {
override fun updateIfEnabled(e: AnActionEvent) {
e.presentation.isEnabled = e.project != null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.utbot.cpp.clion.plugin.actions

import com.intellij.openapi.actionSystem.ActionPlaces
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.ToggleAction
import com.intellij.openapi.project.Project
import org.utbot.cpp.clion.plugin.UTBot
import org.utbot.cpp.clion.plugin.settings.settings

class TogglePluginAction : ToggleAction() {
override fun isSelected(e: AnActionEvent): Boolean {
val project = e.project ?: return false
updateActionText(e, project)
return project.settings.storedSettings.isPluginEnabled
}

override fun setSelected(e: AnActionEvent, pluginEnabled: Boolean) {
val project = e.project ?: return
val previousValue = project.settings.storedSettings.isPluginEnabled
project.settings.storedSettings.isPluginEnabled = pluginEnabled
updateActionText(e, project)
if (previousValue != pluginEnabled)
project.settings.fireUTBotEnabledStateChanged()
}

private fun updateActionText(e: AnActionEvent, project: Project) {
val isPluginEnabled = project.settings.storedSettings.isPluginEnabled
var newText = if (isPluginEnabled) UTBot.message("actions.enable.enabled")
else UTBot.message("actions.enable.disabled")
if (ActionPlaces.isPopupPlace(e.place)) {
newText = if (isPluginEnabled)
UTBot.message("actions.enable.menu.enabled")
else UTBot.message("actions.enable.menu.disabled")
}
e.presentation.text = newText
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.utbot.cpp.clion.plugin.actions

import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import javax.swing.Icon
import org.utbot.cpp.clion.plugin.utils.isPluginEnabled

abstract class UTBotBaseAction(
text: () -> String? = { null },
description: () -> String? = { null },
icon: Icon? = null
) : AnAction(text, description, icon) {
constructor(text: String) : this({ text })

override fun update(e: AnActionEvent) {
if (isPluginEnabled(e)) {
updateIfEnabled(e)
} else {
e.presentation.isEnabledAndVisible = false
}
}

abstract fun updateIfEnabled(e: AnActionEvent)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.utbot.cpp.clion.plugin.actions

import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.ToggleAction
import javax.swing.Icon
import org.utbot.cpp.clion.plugin.utils.isPluginEnabled

abstract class UTBotBaseToggleAction(
text: () -> String? = { null },
description: () -> String? = { null },
icon: Icon? = null
): ToggleAction(text, description, icon) {
override fun update(e: AnActionEvent) {
super.update(e)
if (isPluginEnabled(e)) {
updateIfEnabled(e)
} else {
e.presentation.isEnabledAndVisible = false
}
}

abstract fun updateIfEnabled(e: AnActionEvent)
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package org.utbot.cpp.clion.plugin.actions.configure

import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import org.utbot.cpp.clion.plugin.UTBot
import org.utbot.cpp.clion.plugin.actions.UTBotBaseAction
import org.utbot.cpp.clion.plugin.client.requests.CheckProjectConfigurationRequest
import org.utbot.cpp.clion.plugin.utils.activeProject

class ConfigureProjectAction : AnAction(UTBot.message("projectConfigure.configure")) {
class ConfigureProjectAction : UTBotBaseAction(UTBot.message("projectConfigure.configure")) {

override fun actionPerformed(e: AnActionEvent) = CheckProjectConfigurationRequest(e.activeProject()).executeUsingCurrentClient()
override fun actionPerformed(e: AnActionEvent) = CheckProjectConfigurationRequest(e.activeProject()).execute()

override fun update(e: AnActionEvent) {
override fun updateIfEnabled(e: AnActionEvent) {
e.presentation.isEnabledAndVisible = e.project != null
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
package org.utbot.cpp.clion.plugin.actions.configure

import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import org.utbot.cpp.clion.plugin.UTBot
import org.utbot.cpp.clion.plugin.actions.UTBotBaseAction
import org.utbot.cpp.clion.plugin.grpc.getProjectConfigGrpcRequest
import org.utbot.cpp.clion.plugin.client.requests.CheckProjectConfigurationRequest
import org.utbot.cpp.clion.plugin.utils.activeProject
import testsgen.Testgen

class ReconfigureProjectAction: AnAction(UTBot.message("projectConfigure.reconfigure")) {
class ReconfigureProjectAction: UTBotBaseAction(UTBot.message("projectConfigure.reconfigure")) {

override fun actionPerformed(e: AnActionEvent) {
val project = e.activeProject()
CheckProjectConfigurationRequest(
getProjectConfigGrpcRequest(project, Testgen.ConfigMode.ALL),
project,
).executeUsingCurrentClient()
).execute()
}

override fun update(e: AnActionEvent) {
override fun updateIfEnabled(e: AnActionEvent) {
e.presentation.isEnabledAndVisible = e.project != null
}
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package org.utbot.cpp.clion.plugin.actions.generate

import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import org.utbot.cpp.clion.plugin.utils.currentClient
import org.utbot.cpp.clion.plugin.actions.UTBotBaseAction
import org.utbot.cpp.clion.plugin.utils.client

abstract class BaseGenerateTestsAction : AnAction() {
abstract class BaseGenerateTestsAction : UTBotBaseAction() {

override fun update(e: AnActionEvent) {
override fun updateIfEnabled(e: AnActionEvent) {
val isDefined: Boolean = isDefined(e)

e.presentation.isVisible = isDefined
e.presentation.isEnabled = isDefined && e.currentClient.isServerAvailable()
e.presentation.isEnabled = isDefined && e.client.isServerAvailable()
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class GenerateForAssertionAction : BaseGenerateTestsAction() {
AssertionRequest(
getAssertionGrpcRequest(e),
e.activeProject(),
).executeUsingCurrentClient()
).execute()

override fun isDefined(e: AnActionEvent): Boolean {
val project = e.project
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class GenerateForClassAction : BaseGenerateTestsAction() {
ClassRequest(
getClassGrpcRequest(e),
e.activeProject(),
).executeUsingCurrentClient()
).execute()

override fun isDefined(e: AnActionEvent): Boolean {
val project = e.project
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class GenerateForFileAction : BaseGenerateTestsAction() {
FileRequest(
getFileGrpcRequest(e),
e.activeProject(),
).executeUsingCurrentClient()
).execute()

// action is available only if the selected file ends in .cpp, .hpp, .c or .h
override fun isDefined(e: AnActionEvent): Boolean {
Expand Down
Loading

0 comments on commit 9314a3f

Please sign in to comment.