diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/DatabaseCommandFactory.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/DatabaseCommandFactory.kt index d5dd4572..ef059794 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/DatabaseCommandFactory.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/DatabaseCommandFactory.kt @@ -5,10 +5,7 @@ import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle import com.jetbrains.rider.plugins.efcore.cli.api.models.DotnetEfVersion -import com.jetbrains.rider.plugins.efcore.cli.execution.CommonOptions -import com.jetbrains.rider.plugins.efcore.cli.execution.DotnetCommand -import com.jetbrains.rider.plugins.efcore.cli.execution.EfCoreCommandBuilder -import com.jetbrains.rider.plugins.efcore.cli.execution.KnownEfCommands +import com.jetbrains.rider.plugins.efcore.cli.execution.* import org.jetbrains.annotations.NonNls @Service(Service.Level.PROJECT) @@ -18,17 +15,27 @@ class DatabaseCommandFactory(private val intellijProject: Project) { fun getInstance(project: Project) = project.service() } - fun update(efCoreVersion: DotnetEfVersion, options: CommonOptions, targetMigration: String, connectionString: String? = null): DotnetCommand = - EfCoreCommandBuilder(intellijProject, KnownEfCommands.Database.update, options, EfCoreUiBundle.message("update.database.presentable.name")).apply { + fun update(efCoreVersion: DotnetEfVersion, options: CommonOptions, targetMigration: String, connectionString: String? = null): CliCommand { + val presentation = CliCommandPresentationInfo( + EfCoreUiBundle.message("update.database.presentable.name"), + EfCoreUiBundle.message("database.has.been.updated")) + + return EfCoreCommandBuilder(intellijProject, KnownEfCommands.Database.update, options, presentation).apply { add(targetMigration) if (efCoreVersion.major >= 5 && connectionString != null) { addNamed("--connection", connectionString) } }.build() + } - fun drop(options: CommonOptions): DotnetCommand = - EfCoreCommandBuilder(intellijProject, KnownEfCommands.Database.drop, options, EfCoreUiBundle.message("drop.database.presentable.name")).apply { + fun drop(options: CommonOptions): CliCommand { + val presentation = CliCommandPresentationInfo( + EfCoreUiBundle.message("drop.database.presentable.name"), + EfCoreUiBundle.message("database.has.been.deleted")) + + return EfCoreCommandBuilder(intellijProject, KnownEfCommands.Database.drop, options, presentation).apply { add("--force") }.build() + } } \ No newline at end of file diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/DbContextCommandFactory.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/DbContextCommandFactory.kt index 5cb18d9a..3d7dc1ce 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/DbContextCommandFactory.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/DbContextCommandFactory.kt @@ -5,10 +5,7 @@ import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle import com.jetbrains.rider.plugins.efcore.cli.api.models.DotnetEfVersion -import com.jetbrains.rider.plugins.efcore.cli.execution.CommonOptions -import com.jetbrains.rider.plugins.efcore.cli.execution.DotnetCommand -import com.jetbrains.rider.plugins.efcore.cli.execution.EfCoreCommandBuilder -import com.jetbrains.rider.plugins.efcore.cli.execution.KnownEfCommands +import com.jetbrains.rider.plugins.efcore.cli.execution.* @Service(Service.Level.PROJECT) class DbContextCommandFactory(private val intellijProject: Project) { @@ -19,8 +16,12 @@ class DbContextCommandFactory(private val intellijProject: Project) { fun scaffold(efCoreVersion: DotnetEfVersion, options: CommonOptions, connection: String, provider: String, outputFolder: String, useAttributes: Boolean, useDatabaseNames: Boolean, generateOnConfiguring: Boolean, usePluralizer: Boolean, dbContextName: String, dbContextFolder: String, scaffoldAllTables: Boolean, - tablesList: List, scaffoldAllSchemas: Boolean, schemasList: List): DotnetCommand = - EfCoreCommandBuilder(intellijProject, KnownEfCommands.DbContext.scaffold, options, EfCoreUiBundle.message("scaffold.dbcontext.presentable.name")).apply { + tablesList: List, scaffoldAllSchemas: Boolean, schemasList: List): CliCommand { + val presentation = CliCommandPresentationInfo( + EfCoreUiBundle.message("scaffold.dbcontext.presentable.name"), + EfCoreUiBundle.message("dbcontext.has.been.scaffolded")) + + return EfCoreCommandBuilder(intellijProject, KnownEfCommands.DbContext.scaffold, options, presentation).apply { add(connection) add(provider) @@ -49,4 +50,5 @@ class DbContextCommandFactory(private val intellijProject: Project) { addIf("--no-pluralize", !usePluralizer) } }.build() + } } \ No newline at end of file diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/ManagementCommandFactory.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/ManagementCommandFactory.kt index 718b3682..53e04f8a 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/ManagementCommandFactory.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/ManagementCommandFactory.kt @@ -4,7 +4,8 @@ import com.intellij.openapi.components.Service import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle -import com.jetbrains.rider.plugins.efcore.cli.execution.DotnetCommand +import com.jetbrains.rider.plugins.efcore.cli.execution.CliCommand +import com.jetbrains.rider.plugins.efcore.cli.execution.CliCommandPresentationInfo import com.jetbrains.rider.plugins.efcore.cli.execution.DotnetCommandBuilder @Service(Service.Level.PROJECT) @@ -13,11 +14,16 @@ class ManagementCommandFactory(private val intellijProject: Project) { fun getInstance(project: Project) = project.service() } - fun installEfCoreTools(): DotnetCommand = - DotnetCommandBuilder(EfCoreUiBundle.message("install.dotnet.tool.presentable.name"), intellijProject, "tool", "install").apply { + fun installEfCoreTools(): CliCommand { + val presentation = CliCommandPresentationInfo( + EfCoreUiBundle.message("install.dotnet.tool.presentable.name"), + EfCoreUiBundle.message("ef.core.global.tools.have.been.successfully.installed")) + + return DotnetCommandBuilder(presentation, intellijProject, "tool", "install").apply { add("--ignore-failed-sources") addNamed("--add-source", "https://api.nuget.org/v3/index.json") add("--global") add("dotnet-ef") }.build() + } } \ No newline at end of file diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/MigrationsCommandFactory.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/MigrationsCommandFactory.kt index 97982c14..a8ea0d70 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/MigrationsCommandFactory.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/MigrationsCommandFactory.kt @@ -13,21 +13,35 @@ class MigrationsCommandFactory(private val intellijProject: Project) { fun getInstance(project: Project) = project.service() } - fun add(options: CommonOptions, migrationName: String, outputDirectory: String? = null, namespace: String? = null): DotnetCommand = - EfCoreCommandBuilder(intellijProject, KnownEfCommands.Migrations.add, options, EfCoreUiBundle.message("add.migration.presentable.name")).apply { + fun add(options: CommonOptions, migrationName: String, outputDirectory: String? = null, namespace: String? = null): CliCommand { + val presentation = CliCommandPresentationInfo( + EfCoreUiBundle.message("add.migration.presentable.name"), + EfCoreUiBundle.message("new.migration.has.been.created")) + + return EfCoreCommandBuilder(intellijProject, KnownEfCommands.Migrations.add, options, presentation).apply { add(migrationName) addNamedNullable("--output-dir", outputDirectory) addNamedNullable("--namespace", namespace) }.build() + } + + fun removeLast(options: CommonOptions): CliCommand { + val presentation = CliCommandPresentationInfo( + EfCoreUiBundle.message("remove.last.migration.presentable.name"), + EfCoreUiBundle.message("last.migration.has.been.removed")) - fun removeLast(options: CommonOptions): DotnetCommand = - EfCoreCommandBuilder(intellijProject, KnownEfCommands.Migrations.remove, options, EfCoreUiBundle.message("remove.last.migration.presentable.name")).apply { + return EfCoreCommandBuilder(intellijProject, KnownEfCommands.Migrations.remove, options, presentation).apply { add("--force") }.build() + } fun generateScript(efCoreVersion: DotnetEfVersion, options: CommonOptions, fromMigration: String, toMigration: String?, - outputFile: String, idempotent: Boolean, noTransactions: Boolean): DotnetCommand = - EfCoreCommandBuilder(intellijProject, KnownEfCommands.Migrations.script, options, EfCoreUiBundle.message("generate.sql.script.presentable.name")).apply { + outputFile: String, idempotent: Boolean, noTransactions: Boolean): CliCommand { + val presentation = CliCommandPresentationInfo( + EfCoreUiBundle.message("generate.sql.script.presentable.name"), + EfCoreUiBundle.message("script.has.been.generated")) + + return EfCoreCommandBuilder(intellijProject, KnownEfCommands.Migrations.script, options, presentation).apply { add(fromMigration) addNullable(toMigration) addNamed("--output", outputFile) @@ -36,4 +50,5 @@ class MigrationsCommandFactory(private val intellijProject: Project) { addIf("--no-transactions", noTransactions) } }.build() + } } \ No newline at end of file diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/models/DotnetEfVersion.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/models/DotnetEfVersion.kt index 26e3adec..93b321b3 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/models/DotnetEfVersion.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/api/models/DotnetEfVersion.kt @@ -22,4 +22,8 @@ data class DotnetEfVersion(val major: Int, val minor: Int, val patch: Int) { return DotnetEfVersion(major.toInt(), minor.toInt(), patch.toInt()) } } + + override fun toString(): String { + return "$major.$minor.$patch" + } } diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/DotnetCommand.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/CliCommand.kt similarity index 59% rename from src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/DotnetCommand.kt rename to src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/CliCommand.kt index ac8bfd44..30c6ca79 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/DotnetCommand.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/CliCommand.kt @@ -1,11 +1,9 @@ package com.jetbrains.rider.plugins.efcore.cli.execution import com.intellij.execution.configurations.GeneralCommandLine -import com.intellij.openapi.util.NlsContexts -open class DotnetCommand( +open class CliCommand( val dotnetPath: String, val commandLine: GeneralCommandLine, - @NlsContexts.TabTitle - val presentableName: String + val presentationInfo: CliCommandPresentationInfo ) \ No newline at end of file diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/CliCommandExecutor.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/CliCommandExecutor.kt index fbef4f0b..fb563f38 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/CliCommandExecutor.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/CliCommandExecutor.kt @@ -1,9 +1,32 @@ package com.jetbrains.rider.plugins.efcore.cli.execution +import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFileManager abstract class CliCommandExecutor( protected val intellijProject: Project ) { - abstract fun execute(command: DotnetCommand, resultProcessor: CliCommandResultProcessor? = null) + companion object { + val logger = logger() + } + + suspend fun execute(command: CliCommand): CliCommandResult? { + try { + return doExecute(command) + } + catch (t: Throwable) { + logger.error(t) + return null + } + finally { + refreshSolution() + } + } + + protected abstract suspend fun doExecute(command: CliCommand): CliCommandResult? + + private fun refreshSolution() { + VirtualFileManager.getInstance().refreshWithoutFileWatcher(true) + } } \ No newline at end of file diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/CliCommandPresentationInfo.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/CliCommandPresentationInfo.kt new file mode 100644 index 00000000..ef22fb71 --- /dev/null +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/CliCommandPresentationInfo.kt @@ -0,0 +1,10 @@ +package com.jetbrains.rider.plugins.efcore.cli.execution + +import com.intellij.openapi.util.NlsContexts + +data class CliCommandPresentationInfo( + @NlsContexts.TabTitle + val name: String, + @NlsContexts.NotificationContent + val onSuccessNotification: String +) \ No newline at end of file diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/CliCommandResultProcessor.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/CliCommandResultProcessor.kt deleted file mode 100644 index 34fa1341..00000000 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/CliCommandResultProcessor.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.jetbrains.rider.plugins.efcore.cli.execution - -import com.jetbrains.observables.Event - -abstract class CliCommandResultProcessor { - private val postExecutedEvent = com.jetbrains.observables.Event() - - fun process(result: CliCommandResult, retryAction: () -> Unit) { - doProcess(result, retryAction) - postExecutedEvent.invoke(result) - } - protected abstract fun doProcess(result: CliCommandResult, retryAction: () -> Unit) - fun withPostExecuted(action: (CliCommandResult) -> Unit): CliCommandResultProcessor { - postExecutedEvent += action - - return this - } -} diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/DotnetCommandBuilder.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/DotnetCommandBuilder.kt index 59a41e6c..fc4dc1ba 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/DotnetCommandBuilder.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/DotnetCommandBuilder.kt @@ -12,7 +12,7 @@ import java.io.File import java.nio.charset.Charset open class DotnetCommandBuilder( - private val presentableName: String, + private val presentation: CliCommandPresentationInfo, private val intellijProject: Project, vararg baseCommands: @NonNls String ) { @@ -58,7 +58,7 @@ open class DotnetCommandBuilder( generalCommandLine = generalCommandLine.withParameters(name, value) } - open fun build() = DotnetCommand(generalCommandLine.exePath, generalCommandLine, presentableName) + open fun build() = CliCommand(generalCommandLine.exePath, generalCommandLine, presentation) private fun getDotnetExePath() = activeRuntime?.dotNetCliExePath diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/EfCoreCommandBuilder.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/EfCoreCommandBuilder.kt index 3806389f..8eff8b9e 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/EfCoreCommandBuilder.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/EfCoreCommandBuilder.kt @@ -8,9 +8,9 @@ class EfCoreCommandBuilder( intellijProject: Project, baseCommand: String, private val commonOptions: CommonOptions, - presentableName: String + presentation: CliCommandPresentationInfo ) : DotnetCommandBuilder( - presentableName, + presentation, intellijProject, KnownEfCommands.ef, baseCommand ) { @@ -24,7 +24,7 @@ class EfCoreCommandBuilder( addIf("--verbose", commonOptions.enableDiagnosticLogging) } - override fun build(): DotnetCommand { + override fun build(): CliCommand { val command = super.build() var commandLine = command.commandLine if (commonOptions.additionalArguments.isNotEmpty()) { @@ -32,7 +32,7 @@ class EfCoreCommandBuilder( commandLine = commandLine.withRawParameters(commonOptions.additionalArguments) } - return DotnetCommand(command.dotnetPath, commandLine, command.presentableName) + return CliCommand(command.dotnetPath, commandLine, command.presentationInfo) } private fun makeRelativeProjectPath(projectDirectory: String): String { diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/EfCoreConsoleToolWindowProvider.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/EfCoreConsoleToolWindowProvider.kt index 53ffd34a..1a5fcf3f 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/EfCoreConsoleToolWindowProvider.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/EfCoreConsoleToolWindowProvider.kt @@ -23,10 +23,10 @@ class EfCoreConsoleToolWindowProvider(intellijProject: Project) { } } - fun createTab(command: DotnetCommand, console: ConsoleView) { + fun createTab(command: CliCommand, console: ConsoleView) { val contentManager = toolWindow.contentManager val factory = contentManager.factory - val content = factory.createContent(console.component, command.presentableName, true) + val content = factory.createContent(console.component, command.presentationInfo.name, true) contentManager.addContent(content) toolWindow.activate { contentManager.setSelectedContent(content) diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/NotificationCommandResultProcessor.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/NotificationCommandResultProcessor.kt deleted file mode 100644 index 4dd7b883..00000000 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/NotificationCommandResultProcessor.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.jetbrains.rider.plugins.efcore.cli.execution - -import com.intellij.notification.NotificationGroupManager -import com.intellij.notification.NotificationType -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.project.Project -import com.intellij.openapi.vfs.VfsUtil -import com.intellij.openapi.vfs.VirtualFileManager -import com.jetbrains.rider.projectView.solutionDirectory -import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle -import com.jetbrains.rider.plugins.efcore.KnownNotificationGroups -import com.jetbrains.rider.plugins.efcore.features.shared.TryCommandAgainAction - -public class NotificationCommandResultProcessor( - private val project: Project, - private val succeedText: String, - private val shouldRefreshSolution: Boolean = true -) : CliCommandResultProcessor() { - - override fun doProcess(result: CliCommandResult, retryAction: () -> Unit) { - if (shouldRefreshSolution) { - ApplicationManager.getApplication().invokeAndWait { - refreshSolution() - } - } - - if (result.succeeded) { - NotificationGroupManager.getInstance().getNotificationGroup(KnownNotificationGroups.efCore) - .createNotification(succeedText, NotificationType.INFORMATION) - .notify(project) - } else { - val errorTextBuilder = StringBuilder() - errorTextBuilder.append(EfCoreUiBundle.message("initial.command", result.command)) - - if (result.output.trim().isNotEmpty()) - errorTextBuilder.append("\n\n${EfCoreUiBundle.message("output", result.output)}") - - if (result.error?.trim()?.isNotEmpty() == true) - errorTextBuilder.append("\n\n${EfCoreUiBundle.message("error", result.error)}") - - errorTextBuilder.append("\n\n${EfCoreUiBundle.message("exit.code", result.exitCode)}") - - NotificationGroupManager.getInstance().getNotificationGroup(KnownNotificationGroups.efCore) - .createNotification( - EfCoreUiBundle.message("notification.title.ef.core.command.failed"), - errorTextBuilder.toString(), - NotificationType.ERROR - ) - .addAction(TryCommandAgainAction(retryAction)) - .notify(project) - } - } - - private fun refreshSolution() { - VirtualFileManager.getInstance().refreshWithoutFileWatcher(true) - } -} \ No newline at end of file diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/SilentCommandExecutor.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/SilentCommandExecutor.kt index 1163c688..31e954d2 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/SilentCommandExecutor.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/SilentCommandExecutor.kt @@ -1,40 +1,76 @@ package com.jetbrains.rider.plugins.efcore.cli.execution import com.intellij.execution.util.ExecUtil -import com.intellij.openapi.progress.runBackgroundableTask +import com.intellij.notification.NotificationGroupManager +import com.intellij.notification.NotificationType +import com.intellij.openapi.progress.withBackgroundProgress import com.intellij.openapi.project.Project +import com.intellij.platform.util.progress.indeterminateStep import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle +import com.jetbrains.rider.plugins.efcore.KnownNotificationGroups +import com.jetbrains.rider.plugins.efcore.features.shared.TryCommandAgainAction +import kotlinx.coroutines.suspendCancellableCoroutine import java.io.IOException +@Suppress("UnstableApiUsage", "OPT_IN_USAGE") class SilentCommandExecutor( intellijProject: Project ) : CliCommandExecutor(intellijProject) { - override fun execute(command: DotnetCommand, resultProcessor: CliCommandResultProcessor?) { - runBackgroundableTask(EfCoreUiBundle.message("progress.title.executing.ef.core.command"), intellijProject, false) { - try { - val executionResult = ExecUtil.execAndGetOutput(command.commandLine) - val output = executionResult.stdout - val error = executionResult.stderr - val exitCode = executionResult.exitCode - - resultProcessor?.process( - CliCommandResult( + override suspend fun doExecute(command: CliCommand): CliCommandResult { + return withBackgroundProgress(intellijProject, EfCoreUiBundle.message("progress.title.executing.ef.core.command"), false) { + indeterminateStep { + try { + val executionResult = ExecUtil.execAndGetOutput(command.commandLine) + val output = executionResult.stdout + val error = executionResult.stderr + val exitCode = executionResult.exitCode + val result = CliCommandResult( command.commandLine.commandLineString, exitCode, output, exitCode == 0, error ) - ) { - execute(command, resultProcessor) - } - } catch (e: IOException) { - e.printStackTrace() - resultProcessor?.process(CliCommandResult(command.commandLine.commandLineString, -1, e.toString(), false)) { - execute(command, resultProcessor) + notify(command, result) + result + } catch (e: IOException) { + e.printStackTrace() + + val result = CliCommandResult(command.commandLine.commandLineString, -1, e.toString(), false) + notify(command, result) + result } } } } + + private fun notify(command: CliCommand, result: CliCommandResult) { + if (result.succeeded) { + NotificationGroupManager.getInstance().getNotificationGroup(KnownNotificationGroups.efCore) + .createNotification(command.presentationInfo.onSuccessNotification, NotificationType.INFORMATION) + .notify(intellijProject) + } else { + val errorText = buildString { + append(EfCoreUiBundle.message("initial.command", result.command)) + + if (result.output.trim().isNotEmpty()) + append("\n\n${EfCoreUiBundle.message("output", result.output)}") + + if (result.error?.trim()?.isNotEmpty() == true) + append("\n\n${EfCoreUiBundle.message("error", result.error)}") + + append("\n\n${EfCoreUiBundle.message("exit.code", result.exitCode)}") + } + + NotificationGroupManager.getInstance().getNotificationGroup(KnownNotificationGroups.efCore) + .createNotification( + EfCoreUiBundle.message("notification.title.ef.core.command.failed"), + errorText, + NotificationType.ERROR + ) + .addAction(TryCommandAgainAction { execute(command) }) + .notify(intellijProject) + } + } } \ No newline at end of file diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/TerminalCommandExecutor.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/TerminalCommandExecutor.kt index 4277945b..0b92143a 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/TerminalCommandExecutor.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/cli/execution/TerminalCommandExecutor.kt @@ -1,38 +1,36 @@ package com.jetbrains.rider.plugins.efcore.cli.execution import com.intellij.execution.process.* -import com.intellij.execution.ui.ConsoleView import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.project.Project +import com.intellij.openapi.rd.util.withUiContext import com.intellij.openapi.util.Key import com.intellij.terminal.TerminalExecutionConsole -import com.intellij.util.application import com.jetbrains.rider.run.TerminalProcessHandler +import kotlinx.coroutines.* class TerminalCommandExecutor(intellijProject: Project) : CliCommandExecutor(intellijProject) { private val logger = logger() private val toolWindowProvider by lazy { EfCoreConsoleToolWindowProvider.getInstance(intellijProject) } - override fun execute(command: DotnetCommand, resultProcessor: CliCommandResultProcessor?) { - val processHandler = createProcessHandler(command, resultProcessor) { - execute(command, resultProcessor) + override suspend fun doExecute(command: CliCommand): CliCommandResult? { + return try { + val processCompletion = CompletableDeferred() + val processHandler = createProcessHandler(command, processCompletion) + withUiContext { + val consoleView = TerminalExecutionConsole(intellijProject, processHandler) + toolWindowProvider.createTab(command, consoleView) + } + logger.info("Starting process '${command.commandLine.commandLineString}'") + processHandler.startNotify() + processCompletion.await() + } catch (cancellation: CancellationException) { + null } - val consoleView = createConsoleView(processHandler) - toolWindowProvider.createTab(command, consoleView) - - logger.info("Starting process '${command.commandLine.commandLineString}'") - processHandler.startNotify() - } - - private fun createConsoleView(processHandler: ProcessHandler): ConsoleView { - application.assertIsDispatchThread() - - return TerminalExecutionConsole(intellijProject, processHandler) } - private fun createProcessHandler(command: DotnetCommand, - resultProcessor: CliCommandResultProcessor?, - retryAction: () -> Unit): TerminalProcessHandler = + private fun createProcessHandler(command: CliCommand, + completion: CompletableDeferred): TerminalProcessHandler = TerminalProcessHandler(intellijProject, command.commandLine, command.commandLine.commandLineString).apply { addProcessListener(object : ProcessAdapter() { private val ansiDecoder = AnsiEscapeDecoder() @@ -45,7 +43,7 @@ class TerminalCommandExecutor(intellijProject: Project) : CliCommandExecutor(int ansiDecoder.escapeText(text, outputType) { chunk, _ -> val trimmed = chunk.trim() if (trimmed.isNotEmpty()) { - logger.info("${command.presentableName} [$outputType]: $trimmed") + logger.info("${command.presentationInfo.name} [$outputType]: $trimmed") when (outputType) { ProcessOutputTypes.STDOUT -> outputBuilder.append(trimmed) ProcessOutputTypes.STDERR -> errorBuilder.append(trimmed) @@ -63,7 +61,13 @@ class TerminalCommandExecutor(intellijProject: Project) : CliCommandExecutor(int errorBuilder.toString() ) - resultProcessor?.process(result, retryAction) + completion.complete(result) + } + + override fun processNotStarted() { + logger.error("Process wasn't started") + + completion.cancel() } }) } diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/drop/DropDatabaseAction.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/drop/DropDatabaseAction.kt index 6019366b..06e5fc90 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/drop/DropDatabaseAction.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/drop/DropDatabaseAction.kt @@ -1,13 +1,12 @@ package com.jetbrains.rider.plugins.efcore.features.database.drop import com.intellij.openapi.project.Project -import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle import com.jetbrains.rider.plugins.efcore.cli.api.models.DotnetEfVersion import com.jetbrains.rider.plugins.efcore.features.shared.BaseCommandAction import com.jetbrains.rider.plugins.efcore.rd.RiderEfCoreModel import java.util.UUID -class DropDatabaseAction : BaseCommandAction(EfCoreUiBundle.message("database.has.been.deleted")) { +class DropDatabaseAction : BaseCommandAction() { override fun createDialog( intellijProject: Project, toolsVersion: DotnetEfVersion, diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/drop/DropDatabaseDialogWrapper.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/drop/DropDatabaseDialogWrapper.kt index 3dcea8e6..3a86dc8a 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/drop/DropDatabaseDialogWrapper.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/drop/DropDatabaseDialogWrapper.kt @@ -1,12 +1,11 @@ package com.jetbrains.rider.plugins.efcore.features.database.drop -import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.intellij.openapi.ui.showYesNoDialog import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle import com.jetbrains.rider.plugins.efcore.cli.api.DatabaseCommandFactory import com.jetbrains.rider.plugins.efcore.cli.api.models.DotnetEfVersion -import com.jetbrains.rider.plugins.efcore.cli.execution.DotnetCommand +import com.jetbrains.rider.plugins.efcore.cli.execution.CliCommand import com.jetbrains.rider.plugins.efcore.features.shared.dialog.CommonDataContext import com.jetbrains.rider.plugins.efcore.features.shared.dialog.CommonDialogWrapper import java.util.* @@ -45,7 +44,7 @@ class DropDatabaseDialogWrapper( } } - override fun generateCommand(): DotnetCommand { + override fun generateCommand(): CliCommand { val options = getCommonOptions() return databaseCommandFactory.drop(options) } diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/update/UpdateDatabaseAction.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/update/UpdateDatabaseAction.kt index 09efd0f2..9e1b8bb2 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/update/UpdateDatabaseAction.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/update/UpdateDatabaseAction.kt @@ -1,13 +1,12 @@ package com.jetbrains.rider.plugins.efcore.features.database.update import com.intellij.openapi.project.Project -import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle import com.jetbrains.rider.plugins.efcore.cli.api.models.DotnetEfVersion import com.jetbrains.rider.plugins.efcore.features.shared.BaseCommandAction import com.jetbrains.rider.plugins.efcore.rd.RiderEfCoreModel import java.util.* -class UpdateDatabaseAction : BaseCommandAction(EfCoreUiBundle.message("database.has.been.updated")) { +class UpdateDatabaseAction : BaseCommandAction() { override fun createDialog( intellijProject: Project, toolsVersion: DotnetEfVersion, diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/update/UpdateDatabaseDialogWrapper.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/update/UpdateDatabaseDialogWrapper.kt index 3740dc69..7d657b59 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/update/UpdateDatabaseDialogWrapper.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/database/update/UpdateDatabaseDialogWrapper.kt @@ -1,7 +1,5 @@ package com.jetbrains.rider.plugins.efcore.features.database.update -import com.intellij.execution.configurations.GeneralCommandLine -import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.intellij.ui.components.JBCheckBox import com.intellij.ui.dsl.builder.* @@ -16,7 +14,7 @@ import com.jetbrains.observables.ui.dsl.iconComboBox import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle import com.jetbrains.rider.plugins.efcore.cli.api.DatabaseCommandFactory import com.jetbrains.rider.plugins.efcore.cli.api.models.DotnetEfVersion -import com.jetbrains.rider.plugins.efcore.cli.execution.DotnetCommand +import com.jetbrains.rider.plugins.efcore.cli.execution.CliCommand import com.jetbrains.rider.plugins.efcore.features.connections.DbConnectionInfo import com.jetbrains.rider.plugins.efcore.features.shared.dialog.CommonDialogWrapper import com.jetbrains.rider.plugins.efcore.ui.DbConnectionItemRenderer @@ -73,7 +71,7 @@ class UpdateDatabaseDialogWrapper( } } - override fun generateCommand(): DotnetCommand { + override fun generateCommand(): CliCommand { val commonOptions = getCommonOptions() val targetMigration = dataCtx.targetMigration.value!!.trim() val connection = if (dataCtx.useDefaultConnection.value) null else dataCtx.connection.value diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/dbcontext/scaffold/ScaffoldDbContextAction.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/dbcontext/scaffold/ScaffoldDbContextAction.kt index 7385a574..768aa1aa 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/dbcontext/scaffold/ScaffoldDbContextAction.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/dbcontext/scaffold/ScaffoldDbContextAction.kt @@ -1,13 +1,12 @@ package com.jetbrains.rider.plugins.efcore.features.dbcontext.scaffold import com.intellij.openapi.project.Project -import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle import com.jetbrains.rider.plugins.efcore.cli.api.models.DotnetEfVersion import com.jetbrains.rider.plugins.efcore.features.shared.BaseCommandAction import com.jetbrains.rider.plugins.efcore.rd.RiderEfCoreModel import java.util.* -class ScaffoldDbContextAction : BaseCommandAction(EfCoreUiBundle.message("dbcontext.has.been.scaffolded")) { +class ScaffoldDbContextAction : BaseCommandAction() { override fun createDialog( intellijProject: Project, toolsVersion: DotnetEfVersion, diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/dbcontext/scaffold/ScaffoldDbContextDialogWrapper.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/dbcontext/scaffold/ScaffoldDbContextDialogWrapper.kt index b0b41f17..8e5cbd19 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/dbcontext/scaffold/ScaffoldDbContextDialogWrapper.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/dbcontext/scaffold/ScaffoldDbContextDialogWrapper.kt @@ -1,7 +1,5 @@ package com.jetbrains.rider.plugins.efcore.features.dbcontext.scaffold -import com.intellij.execution.configurations.GeneralCommandLine -import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogPanel import com.intellij.ui.TableUtil @@ -20,7 +18,7 @@ import com.jetbrains.observables.ui.dsl.bindSelected import com.jetbrains.observables.ui.dsl.bindText import com.jetbrains.observables.ui.dsl.editableComboBox import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle -import com.jetbrains.rider.plugins.efcore.cli.execution.DotnetCommand +import com.jetbrains.rider.plugins.efcore.cli.execution.CliCommand import com.jetbrains.rider.plugins.efcore.features.connections.DbConnectionInfo import com.jetbrains.rider.plugins.efcore.rd.DbProviderInfo import com.jetbrains.rider.plugins.efcore.ui.DbConnectionItemRenderer @@ -89,7 +87,7 @@ class ScaffoldDbContextDialogWrapper( } } - override fun generateCommand(): DotnetCommand { + override fun generateCommand(): CliCommand { val commonOptions = getCommonOptions() return dbContextCommandFactory.scaffold( diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/eftools/InstallDotnetEfAction.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/eftools/InstallDotnetEfAction.kt index a2ae2e15..6fae8937 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/eftools/InstallDotnetEfAction.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/eftools/InstallDotnetEfAction.kt @@ -2,22 +2,19 @@ package com.jetbrains.rider.plugins.efcore.features.eftools import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.rd.util.launchBackground +import com.intellij.openapi.rd.util.lifetime import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle import com.jetbrains.rider.plugins.efcore.cli.api.ManagementCommandFactory -import com.jetbrains.rider.plugins.efcore.cli.execution.NotificationCommandResultProcessor import com.jetbrains.rider.plugins.efcore.cli.execution.PreferredCommandExecutorProvider class InstallDotnetEfAction : AnAction(EfCoreUiBundle.message("action.fix.text")) { override fun actionPerformed(actionEvent: AnActionEvent) { - val project = actionEvent.project!! + val project = actionEvent.project ?: return val executor = PreferredCommandExecutorProvider.getInstance(project).getExecutor() val command = ManagementCommandFactory.getInstance(project).installEfCoreTools() - val processor = NotificationCommandResultProcessor( - project, - EfCoreUiBundle.message("ef.core.global.tools.have.been.successfully.installed"), - false - ) - - executor.execute(command, processor) + project.lifetime.launchBackground { + executor.execute(command) + } } } \ No newline at end of file diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/add/AddMigrationAction.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/add/AddMigrationAction.kt index ce0f0fa9..0e9ea01d 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/add/AddMigrationAction.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/add/AddMigrationAction.kt @@ -1,13 +1,12 @@ package com.jetbrains.rider.plugins.efcore.features.migrations.add import com.intellij.openapi.project.Project -import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle import com.jetbrains.rider.plugins.efcore.cli.api.models.DotnetEfVersion import com.jetbrains.rider.plugins.efcore.features.shared.BaseCommandAction import com.jetbrains.rider.plugins.efcore.rd.RiderEfCoreModel import java.util.* -class AddMigrationAction : BaseCommandAction(EfCoreUiBundle.message("new.migration.has.been.created")) { +class AddMigrationAction : BaseCommandAction() { override fun createDialog( intellijProject: Project, toolsVersion: DotnetEfVersion, diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/add/AddMigrationDialogWrapper.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/add/AddMigrationDialogWrapper.kt index ceb6f9cd..2861f647 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/add/AddMigrationDialogWrapper.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/add/AddMigrationDialogWrapper.kt @@ -1,6 +1,5 @@ package com.jetbrains.rider.plugins.efcore.features.migrations.add -import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.intellij.ui.components.JBTextField import com.intellij.ui.dsl.builder.AlignX @@ -12,7 +11,7 @@ import com.jetbrains.observables.withLogger import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle import com.jetbrains.rider.plugins.efcore.cli.api.MigrationsCommandFactory import com.jetbrains.rider.plugins.efcore.cli.api.models.DotnetEfVersion -import com.jetbrains.rider.plugins.efcore.cli.execution.DotnetCommand +import com.jetbrains.rider.plugins.efcore.cli.execution.CliCommand import com.jetbrains.rider.plugins.efcore.features.shared.dialog.CommonDialogWrapper import com.jetbrains.rider.plugins.efcore.ui.AnyInputDocumentListener import com.jetbrains.rider.plugins.efcore.ui.textFieldForRelativeFolder @@ -60,7 +59,7 @@ class AddMigrationDialogWrapper( } } - override fun generateCommand(): DotnetCommand { + override fun generateCommand(): CliCommand { val commonOptions = getCommonOptions() val migrationName = dataCtx.migrationName.value.trim() val migrationsOutputFolder = dataCtx.migrationsOutputFolder.value diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/remove/RemoveLastMigrationAction.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/remove/RemoveLastMigrationAction.kt index 0490ac25..a34d3ca9 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/remove/RemoveLastMigrationAction.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/remove/RemoveLastMigrationAction.kt @@ -1,13 +1,12 @@ package com.jetbrains.rider.plugins.efcore.features.migrations.remove import com.intellij.openapi.project.Project -import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle import com.jetbrains.rider.plugins.efcore.cli.api.models.DotnetEfVersion import com.jetbrains.rider.plugins.efcore.features.shared.BaseCommandAction import com.jetbrains.rider.plugins.efcore.rd.RiderEfCoreModel import java.util.* -class RemoveLastMigrationAction : BaseCommandAction(EfCoreUiBundle.message("last.migration.has.been.removed")) { +class RemoveLastMigrationAction : BaseCommandAction() { override fun createDialog( intellijProject: Project, toolsVersion: DotnetEfVersion, diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/remove/RemoveLastMigrationDialogWrapper.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/remove/RemoveLastMigrationDialogWrapper.kt index 6fa7f80a..07daabc3 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/remove/RemoveLastMigrationDialogWrapper.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/remove/RemoveLastMigrationDialogWrapper.kt @@ -6,7 +6,7 @@ import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle import com.jetbrains.rider.plugins.efcore.cli.api.MigrationsCommandFactory import com.jetbrains.rider.plugins.efcore.cli.api.models.DotnetEfVersion import com.jetbrains.rider.plugins.efcore.cli.execution.CliCommandResult -import com.jetbrains.rider.plugins.efcore.cli.execution.DotnetCommand +import com.jetbrains.rider.plugins.efcore.cli.execution.CliCommand import com.jetbrains.rider.plugins.efcore.features.shared.dialog.CommonDialogWrapper import java.util.* @@ -28,7 +28,7 @@ class RemoveLastMigrationDialogWrapper( initUi() } - override fun generateCommand(): DotnetCommand { + override fun generateCommand(): CliCommand { val commonOptions = getCommonOptions() return migrationsCommandFactory.removeLast(commonOptions) diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/script/GenerateScriptAction.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/script/GenerateScriptAction.kt index 2994337a..4eca32dd 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/script/GenerateScriptAction.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/script/GenerateScriptAction.kt @@ -1,13 +1,12 @@ package com.jetbrains.rider.plugins.efcore.features.migrations.script import com.intellij.openapi.project.Project -import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle import com.jetbrains.rider.plugins.efcore.cli.api.models.DotnetEfVersion import com.jetbrains.rider.plugins.efcore.features.shared.BaseCommandAction import com.jetbrains.rider.plugins.efcore.rd.RiderEfCoreModel import java.util.* -class GenerateScriptAction : BaseCommandAction(EfCoreUiBundle.message("script.has.been.generated")) { +class GenerateScriptAction : BaseCommandAction() { override fun createDialog( intellijProject: Project, toolsVersion: DotnetEfVersion, diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/script/GenerateScriptDialogWrapper.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/script/GenerateScriptDialogWrapper.kt index a1a2eb3b..15da11c8 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/script/GenerateScriptDialogWrapper.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/migrations/script/GenerateScriptDialogWrapper.kt @@ -12,7 +12,7 @@ import com.jetbrains.observables.ui.dsl.iconComboBox import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle import com.jetbrains.rider.plugins.efcore.cli.api.MigrationsCommandFactory import com.jetbrains.rider.plugins.efcore.cli.api.models.DotnetEfVersion -import com.jetbrains.rider.plugins.efcore.cli.execution.DotnetCommand +import com.jetbrains.rider.plugins.efcore.cli.execution.CliCommand import com.jetbrains.rider.plugins.efcore.features.shared.dialog.CommonDialogWrapper import com.jetbrains.rider.plugins.efcore.ui.items.MigrationItem import java.util.* @@ -72,7 +72,7 @@ class GenerateScriptDialogWrapper( mappings.migration.fromItem) } - override fun generateCommand(): DotnetCommand { + override fun generateCommand(): CliCommand { val commonOptions = getCommonOptions() val fromMigration = dataCtx.fromMigration.value!!.trim() val toMigration = dataCtx.toMigration.value?.trim() diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/preview/CommandPreviewDialogWrapper.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/preview/CommandPreviewDialogWrapper.kt index 11828321..35022cbb 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/preview/CommandPreviewDialogWrapper.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/preview/CommandPreviewDialogWrapper.kt @@ -4,14 +4,14 @@ import com.intellij.openapi.ui.DialogWrapper import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.panel import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle -import com.jetbrains.rider.plugins.efcore.cli.execution.DotnetCommand +import com.jetbrains.rider.plugins.efcore.cli.execution.CliCommand import com.jetbrains.rider.plugins.efcore.ui.readonlyExpandableTextField import java.awt.Dimension import javax.swing.Action import javax.swing.JComponent class CommandPreviewDialogWrapper( - private val command: DotnetCommand, + private val command: CliCommand, ) : DialogWrapper(true) { init { init() diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/shared/BaseCommandAction.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/shared/BaseCommandAction.kt index f27f340a..c4f5f3fe 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/shared/BaseCommandAction.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/shared/BaseCommandAction.kt @@ -5,12 +5,10 @@ import com.intellij.notification.NotificationType import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent -import com.intellij.openapi.application.ApplicationManager -import com.intellij.openapi.components.service -import com.intellij.openapi.progress.ProgressIndicator -import com.intellij.openapi.progress.ProgressManager -import com.intellij.openapi.progress.Task import com.intellij.openapi.project.Project +import com.intellij.openapi.rd.util.launchOnUi +import com.intellij.openapi.rd.util.lifetime +import com.intellij.openapi.rd.util.withBackgroundContext import com.jetbrains.rider.model.dotNetActiveRuntimeModel import com.jetbrains.rider.plugins.efcore.rd.RiderEfCoreModel import com.jetbrains.rider.plugins.efcore.rd.riderEfCoreModel @@ -18,39 +16,23 @@ import com.jetbrains.rider.projectView.solution import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle import com.jetbrains.rider.plugins.efcore.KnownNotificationGroups import com.jetbrains.rider.plugins.efcore.cli.api.models.DotnetEfVersion -import com.jetbrains.rider.plugins.efcore.cli.execution.NotificationCommandResultProcessor import com.jetbrains.rider.plugins.efcore.cli.execution.PreferredCommandExecutorProvider import com.jetbrains.rider.plugins.efcore.features.eftools.InstallDotnetEfAction import com.jetbrains.rider.plugins.efcore.features.shared.dialog.BaseDialogWrapper import com.jetbrains.rider.plugins.efcore.rd.ToolKind import java.util.UUID -abstract class BaseCommandAction( - private val actionPerformedText: String -) : AnAction() { +abstract class BaseCommandAction : AnAction() { override fun update(actionEvent: AnActionEvent) { actionEvent.presentation.isVisible = actionEvent.isEfCoreActionContext() } override fun actionPerformed(actionEvent: AnActionEvent) { - val intellijProject = actionEvent.project!! - ProgressManager.getInstance().run(object : Task.Backgroundable(actionEvent.project, EfCoreUiBundle.message("progress.title.getting.dotnet.ef.version"), false) { - override fun run(progress: ProgressIndicator) { - val efCoreDefinition = intellijProject.solution.riderEfCoreModel.efToolsDefinition.valueOrNull - val dotnetCliPath = intellijProject.solution.dotNetActiveRuntimeModel.activeRuntime.valueOrNull?.dotNetCliExePath - - if (dotnetCliPath == null) { - notifyDotnetIsNotInstalled(intellijProject) - } else if (efCoreDefinition == null || efCoreDefinition.toolKind == ToolKind.None) { - notifyEfIsNotInstalled(intellijProject) - } else { - val toolsVersion = DotnetEfVersion.parse(efCoreDefinition.version)!! - ApplicationManager.getApplication().invokeLater { - openDialog(actionEvent, toolsVersion) - } - } - } - }) + val intellijProject = actionEvent.project ?: return + intellijProject.lifetime.launchOnUi { + val version = fetchEfCoreVersion(intellijProject) ?: return@launchOnUi + openDialog(actionEvent, version) + } } override fun getActionUpdateThread() = ActionUpdateThread.EDT @@ -61,24 +43,42 @@ abstract class BaseCommandAction( model: RiderEfCoreModel, currentDotnetProjectId: UUID?): BaseDialogWrapper - private fun openDialog(actionEvent: AnActionEvent, efCoreVersion: DotnetEfVersion) { + private fun fetchEfCoreVersion(intellijProject: Project): DotnetEfVersion? { + val efCoreDefinition = intellijProject.solution.riderEfCoreModel.efToolsDefinition.valueOrNull + val dotnetCliPath = intellijProject.solution.dotNetActiveRuntimeModel.activeRuntime.valueOrNull?.dotNetCliExePath + val dotnetEfVersion = efCoreDefinition?.let { DotnetEfVersion.parse(it.version) } + + return when { + dotnetCliPath == null -> { + notifyDotnetIsNotInstalled(intellijProject) + null + } + efCoreDefinition == null || efCoreDefinition.toolKind == ToolKind.None -> { + notifyEfIsNotInstalled(intellijProject) + null + } + dotnetEfVersion == null -> { + notifyUnknownEfIsInstalled(intellijProject, efCoreDefinition.version) + null + } + else -> dotnetEfVersion + } + } + + private suspend fun openDialog(actionEvent: AnActionEvent, efCoreVersion: DotnetEfVersion) { val intellijProject = actionEvent.project ?: return val model = actionEvent.project?.solution?.riderEfCoreModel ?: return val currentDotnetProjectName = actionEvent.actionDotnetProjectId val dialog = createDialog(intellijProject, efCoreVersion, model, currentDotnetProjectName) if (dialog.showAndGet()) { - val executor = intellijProject.service().getExecutor() + val executor = PreferredCommandExecutorProvider.getInstance(intellijProject).getExecutor() val command = dialog.generateCommand() - val processor = NotificationCommandResultProcessor( - intellijProject, - actionPerformedText, - true - ).withPostExecuted { - dialog.postCommandExecute(it) + withBackgroundContext { + executor.execute(command)?.apply { + dialog.postCommandExecute(this) + } } - - executor.execute(command, processor) } } @@ -89,6 +89,13 @@ abstract class BaseCommandAction( .notify(intellijProject) } + private fun notifyUnknownEfIsInstalled(intellijProject: Project, version: String) { + NotificationGroupManager.getInstance().getNotificationGroup(KnownNotificationGroups.efCore) + .createNotification(EfCoreUiBundle.message("notification.content.invalid.ef.core.tools.version", version), NotificationType.ERROR) + .addAction(InstallDotnetEfAction()) + .notify(intellijProject) + } + private fun notifyDotnetIsNotInstalled(intellijProject: Project) { NotificationGroupManager.getInstance().getNotificationGroup(KnownNotificationGroups.efCore) .createNotification(EfCoreUiBundle.message("notification.content.net.net.core.required.to.execute.this.action"), NotificationType.ERROR) diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/shared/TryCommandAgainAction.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/shared/TryCommandAgainAction.kt index 9a849e76..9c65842f 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/shared/TryCommandAgainAction.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/shared/TryCommandAgainAction.kt @@ -2,12 +2,17 @@ package com.jetbrains.rider.plugins.efcore.features.shared import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.rd.util.launchBackground +import com.intellij.openapi.rd.util.lifetime import com.jetbrains.rider.plugins.efcore.EfCoreUiBundle class TryCommandAgainAction( - private val retryAction: () -> Unit + private val retryAction: suspend () -> Unit ): AnAction(EfCoreUiBundle.message("action.try.again.text")) { - override fun actionPerformed(p0: AnActionEvent) { - retryAction() + override fun actionPerformed(e: AnActionEvent) { + val project = e.project ?: return + project.lifetime.launchBackground { + retryAction() + } } } \ No newline at end of file diff --git a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/shared/dialog/BaseDialogWrapper.kt b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/shared/dialog/BaseDialogWrapper.kt index 03c34b9f..f741af82 100644 --- a/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/shared/dialog/BaseDialogWrapper.kt +++ b/src/rider/main/kotlin/com/jetbrains/rider/plugins/efcore/features/shared/dialog/BaseDialogWrapper.kt @@ -2,10 +2,10 @@ package com.jetbrains.rider.plugins.efcore.features.shared.dialog import com.intellij.openapi.ui.DialogWrapper import com.jetbrains.rider.plugins.efcore.cli.execution.CliCommandResult -import com.jetbrains.rider.plugins.efcore.cli.execution.DotnetCommand +import com.jetbrains.rider.plugins.efcore.cli.execution.CliCommand abstract class BaseDialogWrapper : DialogWrapper(true) { - abstract fun generateCommand(): DotnetCommand + abstract fun generateCommand(): CliCommand open fun postCommandExecute(commandResult: CliCommandResult) {} } \ No newline at end of file diff --git a/src/rider/main/resources/messages/EfCoreUiBundle.properties b/src/rider/main/resources/messages/EfCoreUiBundle.properties index a497c5f4..c5f5cfb4 100644 --- a/src/rider/main/resources/messages/EfCoreUiBundle.properties +++ b/src/rider/main/resources/messages/EfCoreUiBundle.properties @@ -153,8 +153,8 @@ tab.ef.core.command=EF Core Command # Install EF Core tools action.fix.text=Fix ef.core.global.tools.have.been.successfully.installed=EF Core global tools have been successfully installed -progress.title.getting.dotnet.ef.version=Getting dotnet ef version… notification.content.ef.core.tools.are.required.to.execute.this.action=EF Core tools are required to execute this action +notification.content.invalid.ef.core.tools.version=Invalid EF Core tools version: {0} notification.content.net.net.core.required.to.execute.this.action=.NET / .NET Core is required to execute this action notification.title.ef.core.tools.are.not.installed=EF Core tools are not installed