From 11f35f800365fde52ec82628e47a50421439038e Mon Sep 17 00:00:00 2001 From: takahirom Date: Sun, 14 Jul 2024 15:03:40 +0900 Subject: [PATCH] Adjust some idea plugin behaviors * Roborazzi Plugin will use default configuration of IDE * Sort Gradle tasks and select first by default --- .../takahirom/roborazzi/RoborazziTaskType.kt | 18 +++--- roborazzi-idea-plugin/build.gradle.kts | 5 ++ .../idea/preview/PreviewViewModel.kt | 32 ++++++---- .../idea/preview/RoborazziGradleTask.kt | 63 +++++++++++-------- .../idea/preview/RoborazziPreviewTool.kt | 23 ++++++- .../idea/preview/StatusToolbarPanel.kt | 38 +++++++---- 6 files changed, 118 insertions(+), 61 deletions(-) diff --git a/include-build/roborazzi-core/src/commonMain/kotlin/com/github/takahirom/roborazzi/RoborazziTaskType.kt b/include-build/roborazzi-core/src/commonMain/kotlin/com/github/takahirom/roborazzi/RoborazziTaskType.kt index c1d6897c..71ff3084 100644 --- a/include-build/roborazzi-core/src/commonMain/kotlin/com/github/takahirom/roborazzi/RoborazziTaskType.kt +++ b/include-build/roborazzi-core/src/commonMain/kotlin/com/github/takahirom/roborazzi/RoborazziTaskType.kt @@ -1,13 +1,13 @@ package com.github.takahirom.roborazzi @ExperimentalRoborazziApi -enum class RoborazziTaskType { - None, - Record, - Compare, - Verify, - VerifyAndRecord, - CompareAndRecord; +enum class RoborazziTaskType(private val taskName: String) { + None(""), + Record("record"), + Compare("compare"), + Verify("verify"), + VerifyAndRecord("verifyAndRecord"), + CompareAndRecord("compareAndRecord"); fun isEnabled(): Boolean { return this != None @@ -52,5 +52,9 @@ enum class RoborazziTaskType { else -> None } } + + fun getOrderOfTaskName(taskName: String): Int { + return values().indexOfLast { taskName.contains(it.taskName, true) } + } } } \ No newline at end of file diff --git a/roborazzi-idea-plugin/build.gradle.kts b/roborazzi-idea-plugin/build.gradle.kts index 5d011c7b..03393605 100644 --- a/roborazzi-idea-plugin/build.gradle.kts +++ b/roborazzi-idea-plugin/build.gradle.kts @@ -11,6 +11,11 @@ repositories { mavenCentral() } +dependencies { + // Replaced by dependency substitution + implementation("io.github.takahirom.roborazzi:roborazzi-core:0.0.1") +} + // Configure Gradle IntelliJ Plugin // Read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html intellij { diff --git a/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/PreviewViewModel.kt b/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/PreviewViewModel.kt index 6804a56e..66da063c 100644 --- a/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/PreviewViewModel.kt +++ b/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/PreviewViewModel.kt @@ -35,13 +35,13 @@ class PreviewViewModel { val statusText = MutableStateFlow("No images found") private val _dropDownUiState = MutableStateFlow(ActionToolbarUiState()) val dropDownUiState = _dropDownUiState.asStateFlow() - val shouldSeeIndex = MutableStateFlow(-1) + val shouldSeeImageIndex = MutableStateFlow(-1) private var updateListJob: Job? = null private val gradleTask = RoborazziGradleTask() fun onInit(project: Project) { roborazziLog("onInit") - refreshList(project) + refreshImageList(project) } fun onSelectedFileChanged(project: Project) { @@ -64,17 +64,12 @@ class PreviewViewModel { } } - fun executeTaskByName(project: Project, taskName: String) { - roborazziLog("Executing task '$taskName'...") + fun onRefreshButtonClicked(project: Project, selectedTaskName: String) { + roborazziLog("Executing task '$selectedTaskName'...") _dropDownUiState.update { currentUiState -> currentUiState.copy(flag = ActionToolbarUiState.Flag.LOADING) } - gradleTask.executeTaskByName(project, taskName) { - _dropDownUiState.update { currentUiState -> - currentUiState.copy(flag = ActionToolbarUiState.Flag.IDLE) - } - refreshList(project) - } + gradleTask.executeTaskByName(project, selectedTaskName) } private fun selectListIndexByCaret(project: Project) { @@ -92,7 +87,7 @@ class PreviewViewModel { } .let { roborazziLog("shouldSeeIndex.value = $it") - shouldSeeIndex.value = it + shouldSeeImageIndex.value = it } } } @@ -125,7 +120,7 @@ class PreviewViewModel { return methodCnadidate as? KtFunction } - private fun refreshList(project: Project) { + private fun refreshImageList(project: Project) { updateListJob?.cancel() updateListJob = coroutineScope.launch { refreshListProcess(project) @@ -256,7 +251,18 @@ class PreviewViewModel { } fun onShouldSeeIndexHandled() { - shouldSeeIndex.value = -1 + shouldSeeImageIndex.value = -1 + } + + fun onTaskExecuted(project: Project) { + _dropDownUiState.update { currentUiState -> + currentUiState.copy(flag = ActionToolbarUiState.Flag.IDLE) + } + refreshImageList(project) + // For updating the task list after syncing the gradle task + coroutineScope.launch { + fetchTasks(project) + } } data class ActionToolbarUiState( diff --git a/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/RoborazziGradleTask.kt b/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/RoborazziGradleTask.kt index 6e394616..df50b2fd 100644 --- a/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/RoborazziGradleTask.kt +++ b/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/RoborazziGradleTask.kt @@ -1,20 +1,22 @@ package com.github.takahirom.roborazzi.idea.preview +import com.intellij.execution.ExecutionManager +import com.intellij.execution.RunManager import com.intellij.execution.executors.DefaultRunExecutor +import com.intellij.execution.runners.ExecutionEnvironment +import com.intellij.execution.runners.ProgramRunner import com.intellij.openapi.externalSystem.model.DataNode import com.intellij.openapi.externalSystem.model.ProjectKeys -import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings import com.intellij.openapi.externalSystem.model.project.ModuleData -import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode -import com.intellij.openapi.externalSystem.task.TaskCallback import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil -import com.intellij.openapi.externalSystem.util.ExternalSystemUtil import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.project.Project import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiFile import org.jetbrains.kotlin.idea.util.projectStructure.module import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.plugins.gradle.service.execution.GradleExternalTaskConfigurationType +import org.jetbrains.plugins.gradle.service.execution.GradleRunConfiguration import org.jetbrains.plugins.gradle.util.GradleConstants import org.jetbrains.plugins.gradle.util.GradleUtil @@ -24,46 +26,53 @@ class RoborazziGradleTask { val gradleModuleData = getGradleData(project) ?: return emptyList() return ExternalSystemApiUtil.findAll(gradleModuleData, ProjectKeys.TASK) - .filter { it.data.name.contains("Roborazzi", true) && it.data.name.contains("DirRoborazzi", true).not()} - .map { gradleModuleData.data.id + ":" + it.data.name }.sorted() + .filter { + it.data.name.contains("Roborazzi", true) && it.data.name.contains( + "DirRoborazzi", + true + ).not() && !it.data.name.contains("finalize", true) + } + .map { gradleModuleData.data.id + ":" + it.data.name } + .sortedBy { com.github.takahirom.roborazzi.RoborazziTaskType.getOrderOfTaskName(it) } } fun executeTaskByName( project: Project, taskName: String, - onTaskExecuted:() -> Unit ) { - val settings = ExternalSystemTaskExecutionSettings().apply { - externalProjectPath = project.basePath!! + val runManager = RunManager.getInstance(project) + val configurationFactory = GradleExternalTaskConfigurationType.getInstance().factory + val runConfiguration = runManager.createConfiguration( + "Execute $taskName", + configurationFactory + ) + + val gradleRunConfiguration = runConfiguration.configuration as GradleRunConfiguration + gradleRunConfiguration.settings.apply { + externalProjectPath = project.basePath taskNames = listOf(taskName) externalSystemIdString = GradleConstants.SYSTEM_ID.id } - ExternalSystemUtil.runTask( - settings, - DefaultRunExecutor.EXECUTOR_ID, - project, - GradleConstants.SYSTEM_ID, - object : TaskCallback { - override fun onSuccess() { - roborazziLog("Task '$taskName' executed successfully") - onTaskExecuted() - } + runManager.addConfiguration(runConfiguration) + runManager.selectedConfiguration = runConfiguration - override fun onFailure() { - roborazziLog("Task '$taskName' execution failed") - onTaskExecuted() - } - }, - ProgressExecutionMode.IN_BACKGROUND_ASYNC, - false + val environment = ExecutionEnvironment( + DefaultRunExecutor.getRunExecutorInstance(), + ProgramRunner.getRunner(DefaultRunExecutor.EXECUTOR_ID, gradleRunConfiguration)!!, + runConfiguration, + project ) + + ExecutionManager.getInstance(project).restartRunProfile(environment) + ExecutionManager.getInstance(project).getRunningProcesses() } } private fun getGradleData(project: Project): DataNode? { val editor = FileEditorManager.getInstance(project).selectedTextEditor ?: return null - val psiFile: PsiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.document) ?: return null + val psiFile: PsiFile = + PsiDocumentManager.getInstance(project).getPsiFile(editor.document) ?: return null val ktFile = psiFile as? KtFile ?: return null val module = ktFile.module ?: return null return GradleUtil.findGradleModuleData(module) diff --git a/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/RoborazziPreviewTool.kt b/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/RoborazziPreviewTool.kt index e4ab0fcb..f96ccfe0 100644 --- a/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/RoborazziPreviewTool.kt +++ b/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/RoborazziPreviewTool.kt @@ -1,6 +1,10 @@ package com.github.takahirom.roborazzi.idea.preview import com.github.takahirom.roborazzi.idea.settings.AppSettingsConfigurable +import com.intellij.execution.ExecutionListener +import com.intellij.execution.ExecutionManager +import com.intellij.execution.process.ProcessHandler +import com.intellij.execution.runners.ExecutionEnvironment import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.DefaultActionGroup @@ -25,6 +29,7 @@ import com.intellij.util.containers.SLRUMap import com.intellij.util.messages.MessageBusConnection import kotlinx.coroutines.launch import org.jetbrains.kotlin.codegen.inline.getOrPut +import org.jetbrains.plugins.gradle.service.execution.GradleRunConfiguration import java.awt.BorderLayout import java.awt.Component import java.awt.Image @@ -65,7 +70,7 @@ class RoborazziPreviewToolWindowFactory : ToolWindowFactory { class RoborazziPreviewPanel(project: Project) : JPanel(BorderLayout()) { private val listModel = DefaultListModel>() private val statusGradleTaskPanel = StatusToolbarPanel(project) { taskName -> - viewModel?.executeTaskByName(project, taskName) + viewModel?.onRefreshButtonClicked(project, taskName) } private val statusBar = JBBox.createHorizontalBox().apply { statusGradleTaskPanel.statusLabel = "No images found" @@ -116,6 +121,20 @@ class RoborazziPreviewPanel(project: Project) : JPanel(BorderLayout()) { } } ) + project.messageBus.connect().subscribe(ExecutionManager.EXECUTION_TOPIC, object : ExecutionListener { + override fun processTerminated( + executorId: String, + env: ExecutionEnvironment, + handler: ProcessHandler, + exitCode: Int + ) { + super.processTerminated(executorId, env, handler, exitCode) + if (env.runProfile is GradleRunConfiguration) { + viewModel?.onTaskExecuted(project) + } + } + }) + val scrollPane = JBScrollPane(imageList) scrollPane.verticalScrollBarPolicy = JScrollPane.VERTICAL_SCROLLBAR_ALWAYS add(statusBar, BorderLayout.NORTH) @@ -177,7 +196,7 @@ class RoborazziPreviewPanel(project: Project) : JPanel(BorderLayout()) { } } viewModel?.coroutineScope?.launch { - viewModel?.shouldSeeIndex?.collect { + viewModel?.shouldSeeImageIndex?.collect { if (it == -1) { return@collect } diff --git a/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/StatusToolbarPanel.kt b/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/StatusToolbarPanel.kt index 674a4045..03c8942a 100644 --- a/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/StatusToolbarPanel.kt +++ b/roborazzi-idea-plugin/src/main/kotlin/com/github/takahirom/roborazzi/idea/preview/StatusToolbarPanel.kt @@ -27,14 +27,14 @@ import javax.swing.JComponent class StatusToolbarPanel( project: Project, - onActionClicked: (taskName: String) -> Unit + onTaskExecuteButtonClicked: (taskName: String) -> Unit ) : JBPanel>(GridBagLayout()){ private val _statusLabel = JBLabel() private val actionToolbar = RoborazziGradleTaskToolbar( project = project, place = "RoborazziGradleTaskToolbar", horizontal = true, - onActionClicked = onActionClicked + onTaskExecuteButtonClicked = onTaskExecuteButtonClicked ) var statusLabel: String = "" @@ -97,17 +97,17 @@ class RoborazziGradleTaskToolbar( place: String, actionGroup: DefaultActionGroup = DefaultActionGroup(), horizontal: Boolean, - onActionClicked: (taskName: String) -> Unit + onTaskExecuteButtonClicked: (taskName: String) -> Unit ) : ActionToolbarImpl(place, actionGroup, horizontal) { private val propertiesComponent = PropertiesComponent.getInstance(project) private val roborazziGradleTaskAction = GradleTaskComboBoxAction(propertiesComponent) - private val executeGradleTaskAction = ExecuteGradleTaskAction(propertiesComponent, onActionClicked) + private val executeGradleTaskExecuteAction = ExecuteGradleTaskExecuteAction(propertiesComponent, onTaskExecuteButtonClicked) var isExecuteGradleTaskActionEnabled: Boolean get() = isEnabled set(value) { - executeGradleTaskAction.isActionEnabled = value + executeGradleTaskExecuteAction.isActionEnabled = value isEnabled = value } @@ -115,7 +115,7 @@ class RoborazziGradleTaskToolbar( actionGroup.addAll( listOf( roborazziGradleTaskAction, - executeGradleTaskAction + executeGradleTaskExecuteAction ) ) } @@ -145,7 +145,14 @@ class RoborazziGradleTaskToolbar( override fun createPopupActionGroup(button: JComponent, dataContext: DataContext) = popupActionGroup override fun update(e: AnActionEvent) { - e.presentation.text = propertiesComponent.getValue(SELECTED_TASK_KEY) ?: "Select Task" + + e.presentation.text = propertiesComponent.getValue(SELECTED_TASK_KEY) ?: if(popupActionGroup.childActionsOrStubs.isNotEmpty()) { + val defaultTask = popupActionGroup.childActionsOrStubs[0].templatePresentation.text + propertiesComponent.setSelectedTask(defaultTask) + defaultTask + } else { + "Select Task" + } e.presentation.icon = AllIcons.General.Gear } @@ -153,12 +160,12 @@ class RoborazziGradleTaskToolbar( fun setActions(actions: List) { popupActionGroup.removeAll() - actions.forEach { popupActionGroup.add(GradleTaskAction(it.label, propertiesComponent)) } + actions.forEach { popupActionGroup.add(GradleTaskSelectAction(it.label, propertiesComponent)) } } } - class ExecuteGradleTaskAction( + class ExecuteGradleTaskExecuteAction( private val propertiesComponent: PropertiesComponent, private val onActionClicked: (taskName: String) -> Unit ): DumbAwareAction("Execute Selected Task", "Execute selected task", AllIcons.Actions.Refresh) { @@ -170,7 +177,7 @@ class RoborazziGradleTaskToolbar( } override fun actionPerformed(e: AnActionEvent) { - propertiesComponent.getValue(SELECTED_TASK_KEY)?.let { onActionClicked(it) } + propertiesComponent.getSelectedTask()?.let { onActionClicked(it) } } override fun update(e: AnActionEvent) { @@ -181,14 +188,14 @@ class RoborazziGradleTaskToolbar( override fun getActionUpdateThread() = ActionUpdateThread.EDT } - class GradleTaskAction( + class GradleTaskSelectAction( private val taskName: String, private val propertiesComponent: PropertiesComponent, ): DumbAwareAction(taskName, taskName, AllIcons.General.Gear) { override fun actionPerformed(e: AnActionEvent) { e.presentation.text = taskName - propertiesComponent.setValue(SELECTED_TASK_KEY, taskName) + propertiesComponent.setSelectedTask(taskName) } override fun update(e: AnActionEvent) { @@ -201,5 +208,12 @@ class RoborazziGradleTaskToolbar( companion object { internal const val SELECTED_TASK_KEY = "roborazzi.idea.selectedTask" } +} + +fun PropertiesComponent.getSelectedTask(): String? { + return getValue(RoborazziGradleTaskToolbar.SELECTED_TASK_KEY) +} +fun PropertiesComponent.setSelectedTask(taskName: String) { + setValue(RoborazziGradleTaskToolbar.SELECTED_TASK_KEY, taskName) } \ No newline at end of file