diff --git a/src/main/kotlin/org/move/cli/runConfigurations/AptosCommandLine.kt b/src/main/kotlin/org/move/cli/runConfigurations/AptosCommandLine.kt index 9b98215b0..a7874bad7 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/AptosCommandLine.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/AptosCommandLine.kt @@ -3,7 +3,7 @@ package org.move.cli.runConfigurations import com.intellij.execution.configuration.EnvironmentVariablesData import com.intellij.execution.configurations.GeneralCommandLine import com.intellij.execution.configurations.PtyCommandLine -import com.intellij.openapi.util.text.StringUtil +import com.intellij.util.execution.ParametersListUtil import java.nio.file.Path data class AptosCommandLine( @@ -12,9 +12,8 @@ data class AptosCommandLine( val workingDirectory: Path? = null, val environmentVariables: EnvironmentVariablesData = EnvironmentVariablesData.DEFAULT ) { - fun joinArgs(): String { - return StringUtil.join(subCommand?.split(" ").orEmpty() + arguments, " ") - } + val commandLineString: String + get() = ParametersListUtil.join(subCommand?.split(" ").orEmpty() + arguments) fun toGeneralCommandLine(cliExePath: Path): GeneralCommandLine { val generalCommandLine = GeneralCommandLine() @@ -22,7 +21,7 @@ data class AptosCommandLine( // subcommand can be null .withParameters(subCommand?.split(" ").orEmpty()) .withParameters(this.arguments) - .withWorkDirectory(this.workingDirectory?.toString()) + .withWorkingDirectory(this.workingDirectory) .withCharset(Charsets.UTF_8) this.environmentVariables.configureCommandLine(generalCommandLine, true) return generalCommandLine @@ -35,30 +34,11 @@ data class AptosCommandLine( // subcommand can be null .withParameters(subCommand?.split(" ").orEmpty()) .withParameters(this.arguments) - .withWorkDirectory(this.workingDirectory?.toString()) + .withWorkingDirectory(this.workingDirectory) .withCharset(Charsets.UTF_8) // disables default coloring for stderr .withRedirectErrorStream(true) this.environmentVariables.configureCommandLine(generalCommandLine, true) return generalCommandLine } - -// fun createRunConfiguration( -// moveProject: MoveProject, -// configurationName: String, -// save: Boolean -// ): RunnerAndConfigurationSettings { -// val project = moveProject.project -// val runConfiguration = -// AnyCommandConfigurationFactory.createTemplateRunConfiguration( -// project, -// configurationName, -// save = save -// ) -// val anyCommandConfiguration = runConfiguration.configuration as AnyCommandConfiguration -// anyCommandConfiguration.command = this.joinArgs() -// anyCommandConfiguration.workingDirectory = this.workingDirectory -// anyCommandConfiguration.environmentVariables = this.environmentVariables -// return runConfiguration -// } } diff --git a/src/main/kotlin/org/move/cli/runConfigurations/CommandConfigurationBase.kt b/src/main/kotlin/org/move/cli/runConfigurations/CommandConfigurationBase.kt index 58f1a9c63..6d6dd5c78 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/CommandConfigurationBase.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/CommandConfigurationBase.kt @@ -63,10 +63,10 @@ abstract class CommandConfigurationBase( } fun clean(): CleanConfiguration { - val workingDirectory = workingDirectory - ?: return configurationError("No working directory specified") val (subcommand, arguments) = parseAptosCommand(command) ?: return configurationError("No subcommand specified") + val workingDirectory = workingDirectory + ?: return configurationError("No working directory specified") val aptosPath = project.aptosExecPath ?: return configurationError("No Aptos CLI specified") if (!aptosPath.exists()) { diff --git a/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt b/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt index ce05b5e26..160370cd8 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt @@ -115,11 +115,7 @@ data class Aptos(val cliLocation: Path, val parentDisposable: Disposable?): Disp networkUrl: String? = null, connectionTimeoutSecs: Int = -1, nodeApiKey: String? = null, - runner: CapturingProcessHandler.() -> ProcessOutput = { - runProcessWithGlobalProgress( - timeoutInMilliseconds = null - ) - } + runner: CapturingProcessHandler.() -> ProcessOutput = { runProcessWithGlobalProgress() } ): RsProcessResult { val commandLine = AptosCommandLine( subCommand = "move download", @@ -184,11 +180,7 @@ data class Aptos(val cliLocation: Path, val parentDisposable: Disposable?): Disp private fun executeCommandLine( commandLine: AptosCommandLine, listener: ProcessListener? = null, - runner: CapturingProcessHandler.() -> ProcessOutput = { - runProcessWithGlobalProgress( - timeoutInMilliseconds = null - ) - } + runner: CapturingProcessHandler.() -> ProcessOutput = { runProcessWithGlobalProgress() } ): RsProcessResult { return commandLine .toGeneralCommandLine(this.cliLocation) diff --git a/src/main/kotlin/org/move/cli/runConfigurations/producers/CommandConfigurationProducerBase.kt b/src/main/kotlin/org/move/cli/runConfigurations/producers/CommandConfigurationProducerBase.kt index dd752b003..9f3cc5f99 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/producers/CommandConfigurationProducerBase.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/producers/CommandConfigurationProducerBase.kt @@ -26,7 +26,7 @@ abstract class CommandConfigurationProducerBase: templateConfiguration.name = cmdConf.configurationName val commandLine = cmdConf.commandLine - templateConfiguration.command = commandLine.joinArgs() + templateConfiguration.command = commandLine.commandLineString templateConfiguration.workingDirectory = commandLine.workingDirectory var environment = commandLine.environmentVariables.envs @@ -45,7 +45,7 @@ abstract class CommandConfigurationProducerBase: val location = context.psiLocation ?: return false val cmdConf = configFromLocation(location) ?: return false return configuration.name == cmdConf.configurationName - && configuration.command == cmdConf.commandLine.joinArgs() + && configuration.command == cmdConf.commandLine.commandLineString && configuration.workingDirectory == cmdConf.commandLine.workingDirectory && configuration.environmentVariables == cmdConf.commandLine.environmentVariables } diff --git a/src/main/kotlin/org/move/cli/toolwindow/ProjectsTreeMouseListener.kt b/src/main/kotlin/org/move/cli/toolwindow/ProjectsTreeMouseListener.kt index c5def6756..37b686b41 100644 --- a/src/main/kotlin/org/move/cli/toolwindow/ProjectsTreeMouseListener.kt +++ b/src/main/kotlin/org/move/cli/toolwindow/ProjectsTreeMouseListener.kt @@ -1,20 +1,7 @@ package org.move.cli.toolwindow -import com.intellij.diagnostic.PluginException -import com.intellij.execution.Location -import com.intellij.execution.PsiLocation -import com.intellij.execution.actions.RunContextAction -import com.intellij.execution.executors.DefaultRunExecutor -import com.intellij.openapi.actionSystem.ActionPlaces -import com.intellij.openapi.actionSystem.AnActionEvent -import com.intellij.openapi.actionSystem.impl.SimpleDataContext -import com.intellij.openapi.diagnostic.LogLevel.WARNING -import com.intellij.openapi.diagnostic.logger -import com.intellij.psi.NavigatablePsiElement -import com.intellij.psi.PsiElement import com.intellij.util.PsiNavigateUtil import org.move.cli.toolwindow.MoveProjectsTreeStructure.MoveSimpleNode -import org.move.ide.notifications.logOrShowBalloon import java.awt.event.MouseAdapter import java.awt.event.MouseEvent import javax.swing.SwingUtilities @@ -28,40 +15,18 @@ class ProjectsTreeMouseListener: MouseAdapter() { val projectsTree = e.source as? MoveProjectsTree ?: return val treeNode = projectsTree.selectionModel .selectionPath?.lastPathComponent as? DefaultMutableTreeNode ?: return + val userNode = treeNode.userObject when (userNode) { - is MoveSimpleNode.Entrypoint -> navigateToPsiElement(userNode.function) - is MoveSimpleNode.View -> navigateToPsiElement(userNode.function) - is MoveSimpleNode.Module -> navigateToPsiElement(userNode.module) - } - } - - private fun executeContextAction(psiElement: PsiElement) { - val psiLocation = - try { - PsiLocation.fromPsiElement(psiElement) ?: return - } catch (e: PluginException) { - // TODO: figure out why this exception is raised - LOG.logOrShowBalloon(e.message, productionLevel = WARNING) - return + is MoveSimpleNode.Entrypoint -> { + PsiNavigateUtil.navigate(userNode.function) + } + is MoveSimpleNode.View -> { + PsiNavigateUtil.navigate(userNode.function) + } + is MoveSimpleNode.Module -> { + PsiNavigateUtil.navigate(userNode.module) } - val dataContext = - SimpleDataContext.getSimpleContext(Location.DATA_KEY, psiLocation) - val actionEvent = - AnActionEvent.createFromDataContext(ActionPlaces.TOOLWINDOW_CONTENT, null, dataContext) - - val executor = DefaultRunExecutor.getRunExecutorInstance() - RunContextAction(executor).actionPerformed(actionEvent) - } - - private fun navigateToPsiElement(psiElement: PsiElement) { - val navigationElement = psiElement.navigationElement as? NavigatablePsiElement ?: return - if (navigationElement.canNavigate()) { - navigationElement.navigate(true) } } - - companion object { - private val LOG = logger() - } } diff --git a/src/main/kotlin/org/move/openapiext/CommandLineExt.kt b/src/main/kotlin/org/move/openapiext/CommandLineExt.kt index c08230772..8102e0d14 100644 --- a/src/main/kotlin/org/move/openapiext/CommandLineExt.kt +++ b/src/main/kotlin/org/move/openapiext/CommandLineExt.kt @@ -13,51 +13,22 @@ import com.intellij.notification.NotificationType.INFORMATION import com.intellij.openapi.Disposable import com.intellij.openapi.application.runReadAction import com.intellij.openapi.diagnostic.Logger -import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.util.Disposer import com.intellij.util.SystemProperties -import com.intellij.util.io.systemIndependentPath import org.move.cli.runConfigurations.MvCapturingProcessHandler import org.move.cli.settings.isDebugModeEnabled import org.move.ide.notifications.showBalloonWithoutProject import org.move.stdext.RsResult import org.move.stdext.unwrapOrElse -import java.nio.file.Path - -private val LOG = Logger.getInstance("org.move.openapiext.CommandLineExt") - -//@Suppress("FunctionName") -//fun GeneralCommandLine(path: Path, vararg args: String) = -// GeneralCommandLine(path.systemIndependentPath, *args) - -fun GeneralCommandLine.withWorkDirectory(path: Path?) = withWorkDirectory(path?.systemIndependentPath) - -//fun GeneralCommandLine.execute(timeoutInMilliseconds: Int? = 1000): ProcessOutput? { -// val output = try { -// val handler = CapturingProcessHandler(this) -// LOG.info("Executing `$commandLineString`") -// handler.runProcessWithGlobalProgress(timeoutInMilliseconds) -// } catch (e: ExecutionException) { -// LOG.warn("Failed to run executable", e) -// return null -// } -// -// if (!output.isSuccess) { -// LOG.warn(errorMessage(this, output)) -// } -// -// return output -//} fun GeneralCommandLine.execute(): ProcessOutput? { - LOG.info("Executing `$commandLineString`") - val handler = MvCapturingProcessHandler.startProcess(this).unwrapOrElse { - LOG.warn("Failed to run executable", it) - return null + val processHandler = createProcessHandler().unwrapOrNull() ?: return null + + val output = processHandler.runProcessWithGlobalProgress() + if (isDebugModeEnabled()) { + showCommandLineBalloon(output) } - val output = handler.runProcessWithGlobalProgress() - showCommandLineBalloon(commandLineString, output, this.workDirectory?.path) if (!output.isSuccess) { LOG.warn(RsProcessExecutionException.errorMessage(commandLineString, output)) @@ -69,17 +40,10 @@ fun GeneralCommandLine.execute(): ProcessOutput? { fun GeneralCommandLine.execute( owner: Disposable, stdIn: ByteArray? = null, - runner: CapturingProcessHandler.() -> ProcessOutput = { runProcessWithGlobalProgress(timeoutInMilliseconds = null) }, + runner: CapturingProcessHandler.() -> ProcessOutput = { runProcessWithGlobalProgress() }, listener: ProcessListener? = null ): RsProcessResult { - LOG.info("Executing `$commandLineString`") - - val processHandler = MvCapturingProcessHandler - .startProcess(this) // The OS process is started here - .unwrapOrElse { - LOG.warn("Failed to run executable", it) - return RsResult.Err(RsProcessExecutionException.Start(commandLineString, it)) - } + val processHandler = createProcessHandler().unwrapOrElse { return RsResult.Err(it) } // kill process on dispose() val processKiller = Disposable { @@ -128,7 +92,10 @@ fun GeneralCommandLine.execute( } finally { Disposer.dispose(processKiller) } - showCommandLineBalloon(commandLineString, output, this.workDirectory.path) + + if (isDebugModeEnabled()) { + showCommandLineBalloon(output) + } return when { output.isCancelled -> RsResult.Err(RsProcessExecutionException.Canceled(commandLineString, output)) @@ -143,66 +110,19 @@ fun GeneralCommandLine.execute( } } -private fun showCommandLineBalloon( - commandText: String, - output: ProcessOutput, - workingDirectory: String? = null -) { - if (isDebugModeEnabled()) { - val userHome = SystemProperties.getUserHome() - val command = commandText.replace(userHome, "~") - val workDir = workingDirectory?.replace(userHome, "~") - val atWorkDir = if (workDir != null) " 

at $workDir

" else "" - when { - output.isTimeout -> { - showBalloonWithoutProject( - "Execution failed", - "$command (timeout) $atWorkDir", - INFORMATION - ) - } - output.isCancelled -> { - showBalloonWithoutProject( - "Execution failed", - "$command (cancelled) $atWorkDir", - INFORMATION - ) - } - output.exitCode != 0 -> { - showBalloonWithoutProject( - "Execution failed", - "$command (exit code ${output.exitCode}) $atWorkDir " + - "

stdout=${output.stdout}

" + - "

stderr=${output.stderr}

", - INFORMATION - ) - } - else -> { - showBalloonWithoutProject( - "Execution successful", - "$command $atWorkDir", - INFORMATION - ) - } +private fun GeneralCommandLine.createProcessHandler(): RsProcessResult { + LOG.info("Executing `$commandLineString`") + val processHandler = MvCapturingProcessHandler.startProcess(this) + .mapErr { + LOG.warn("Failed to run executable", it) + return RsResult.Err(RsProcessExecutionException.Start(commandLineString, it)) } - } + return processHandler } -private fun errorMessage(commandLine: GeneralCommandLine, output: ProcessOutput): String = """ - |Execution failed (exit code ${output.exitCode}). - |${commandLine.commandLineString} - |stdout : ${output.stdout} - |stderr : ${output.stderr} - """.trimMargin() - fun CapturingProcessHandler.runProcessWithGlobalProgress(timeoutInMilliseconds: Int? = null): ProcessOutput { - return runProcess(ProgressManager.getGlobalProgressIndicator(), timeoutInMilliseconds) -} - -private fun CapturingProcessHandler.runProcess( - indicator: ProgressIndicator?, - timeoutInMilliseconds: Int? = null -): ProcessOutput { + // indicator can be null if ProgressManager not yet initialized + val indicator = ProgressManager.getGlobalProgressIndicator() return when { indicator != null && timeoutInMilliseconds != null -> runProcessWithProgressIndicator(indicator, timeoutInMilliseconds) @@ -213,4 +133,45 @@ private fun CapturingProcessHandler.runProcess( } } -val ProcessOutput.isSuccess: Boolean get() = !isTimeout && !isCancelled && exitCode == 0 +private fun GeneralCommandLine.showCommandLineBalloon(processOutput: ProcessOutput) { + val workingDirectory = this.workingDirectory?.toString() + val userHome = SystemProperties.getUserHome() + val command = commandLineString.replace(userHome, "~") + val workDir = workingDirectory?.replace(userHome, "~") + val atWorkDir = if (workDir != null) " 

at $workDir

" else "" + when { + processOutput.isTimeout -> { + showBalloonWithoutProject( + "Execution failed", + "$command (timeout) $atWorkDir", + INFORMATION + ) + } + processOutput.isCancelled -> { + showBalloonWithoutProject( + "Execution failed", + "$command (cancelled) $atWorkDir", + INFORMATION + ) + } + processOutput.exitCode != 0 -> { + showBalloonWithoutProject( + "Execution failed", + "$command (exit code ${processOutput.exitCode}) $atWorkDir " + + "

stdout=${processOutput.stdout}

" + + "

stderr=${processOutput.stderr}

", + INFORMATION + ) + } + else -> { + showBalloonWithoutProject( + "Execution successful", + "$command $atWorkDir", + INFORMATION + ) + } + } +} + +private val LOG = Logger.getInstance("org.move.openapiext.CommandLineExt") + diff --git a/src/main/kotlin/org/move/openapiext/ProcessOutput.kt b/src/main/kotlin/org/move/openapiext/ProcessOutput.kt new file mode 100644 index 000000000..cfe0c9cae --- /dev/null +++ b/src/main/kotlin/org/move/openapiext/ProcessOutput.kt @@ -0,0 +1,5 @@ +package org.move.openapiext + +import com.intellij.execution.process.ProcessOutput + +val ProcessOutput.isSuccess: Boolean get() = !isTimeout && !isCancelled && exitCode == 0 diff --git a/src/test/kotlin/org/move/toml/MoveTomlErrorAnnotatorTest.kt b/src/test/kotlin/org/move/toml/errorAnnotator/InvalidAddressTest.kt similarity index 65% rename from src/test/kotlin/org/move/toml/MoveTomlErrorAnnotatorTest.kt rename to src/test/kotlin/org/move/toml/errorAnnotator/InvalidAddressTest.kt index b141d01fd..0d6135661 100644 --- a/src/test/kotlin/org/move/toml/MoveTomlErrorAnnotatorTest.kt +++ b/src/test/kotlin/org/move/toml/errorAnnotator/InvalidAddressTest.kt @@ -1,24 +1,28 @@ -package org.move.toml +package org.move.toml.errorAnnotator -import org.move.utils.tests.annotation.AnnotatorTestCase - -class MoveTomlErrorAnnotatorTest : AnnotatorTestCase(MoveTomlErrorAnnotator::class) { - fun `test valid addresses`() = checkMoveTomlWarnings(""" +class InvalidAddressTest: MoveTomlAnnotatorTestBase() { + fun `test valid addresses`() = checkMoveTomlWarnings( + """ [addresses] addr0 = "_" addr1 = "0x1" addr2 = "0x42" addr3 = "0x4242424242424242424242424242424242424242424242424242420000000000" addr4 = "4242424242424242424242424242424242424242424242424242420000000000" - """) + """ + ) - fun `test invalid symbols in address`() = checkMoveTomlWarnings(""" + fun `test invalid symbols in address`() = checkMoveTomlWarnings( + """ [addresses] addr2 = "0xhelloworld" - """) + """ + ) - fun `test address is too long`() = checkMoveTomlWarnings(""" + fun `test address is too long`() = checkMoveTomlWarnings( + """ [addresses] addr3 = "0x424242424242424242424242424242424242424242424242424242000000000011122" - """) + """ + ) } \ No newline at end of file diff --git a/src/test/kotlin/org/move/toml/errorAnnotator/MoveTomlAnnotatorTestBase.kt b/src/test/kotlin/org/move/toml/errorAnnotator/MoveTomlAnnotatorTestBase.kt new file mode 100644 index 000000000..2cadae17f --- /dev/null +++ b/src/test/kotlin/org/move/toml/errorAnnotator/MoveTomlAnnotatorTestBase.kt @@ -0,0 +1,14 @@ +package org.move.toml.errorAnnotator + +import org.intellij.lang.annotations.Language +import org.move.toml.MoveTomlErrorAnnotator +import org.move.utils.tests.annotation.AnnotatorTestCase + +abstract class MoveTomlAnnotatorTestBase: AnnotatorTestCase(MoveTomlErrorAnnotator::class) { + protected fun checkMoveTomlWarnings(@Language("TOML") text: String) = + annotationFixture.check(text, + configure = { tomlText -> + annotationFixture.codeInsightFixture + .configureByText("Move.toml", tomlText) + }) +} \ No newline at end of file diff --git a/src/test/kotlin/org/move/utils/tests/annotation/MvAnnotationTestCase.kt b/src/test/kotlin/org/move/utils/tests/annotation/MvAnnotationTestCase.kt index f37065fb4..068b90bb0 100644 --- a/src/test/kotlin/org/move/utils/tests/annotation/MvAnnotationTestCase.kt +++ b/src/test/kotlin/org/move/utils/tests/annotation/MvAnnotationTestCase.kt @@ -23,13 +23,6 @@ abstract class MvAnnotationTestCase : MvTestBase() { fun checkErrors(@Language("Move") text: String) = annotationFixture.checkErrors(text) fun checkWarnings(@Language("Move") text: String) = annotationFixture.checkWarnings(text) - fun checkMoveTomlWarnings(@Language("TOML") text: String) = - annotationFixture.check(text, - configure = { tomlText -> - annotationFixture.codeInsightFixture - .configureByText("Move.toml", tomlText) - }) - protected fun checkByText( @Language("Move") text: String, checkWarn: Boolean = true,