From 62612027ac174c2a58665bc580dfa358e17fcee8 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Tue, 14 May 2024 23:22:42 +0300 Subject: [PATCH 01/14] update bundled aptos version --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index bdc44edbe..70b5e1ea5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -55,7 +55,7 @@ val javaVersion = JavaVersion.VERSION_17 val pluginJarName = "intellij-move-$pluginVersion" val kotlinReflectVersion = "1.8.10" -val aptosVersion = "3.1.0" +val aptosVersion = "3.3.0" val remoteRobotVersion = "0.11.22" From 06a774b0d9a4534bb6810a2154a75837c3e82b71 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Wed, 15 May 2024 00:05:41 +0300 Subject: [PATCH 02/14] reorganize aptos exec code --- .../cli/runConfigurations/BlockchainCli.kt | 199 +----------------- .../runConfigurations/CliCommandLineArgs.kt | 4 +- .../move/cli/runConfigurations/aptos/Aptos.kt | 164 +++++++++++++++ .../org/move/cli/runConfigurations/sui/Sui.kt | 66 ++++++ .../cli/settings/MvProjectSettingsService.kt | 4 +- .../ide/annotator/RsExternalLinterPass.kt | 4 +- .../ide/annotator/RsExternalLinterUtils.kt | 4 +- .../ide/newProject/MoveProjectGenerator.kt | 11 +- .../org/move/openapiext/CommandLineExt.kt | 37 ++-- 9 files changed, 267 insertions(+), 226 deletions(-) create mode 100644 src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt create mode 100644 src/main/kotlin/org/move/cli/runConfigurations/sui/Sui.kt diff --git a/src/main/kotlin/org/move/cli/runConfigurations/BlockchainCli.kt b/src/main/kotlin/org/move/cli/runConfigurations/BlockchainCli.kt index d0192ff50..975086868 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/BlockchainCli.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/BlockchainCli.kt @@ -1,26 +1,13 @@ package org.move.cli.runConfigurations -import com.intellij.execution.configuration.EnvironmentVariablesData import com.intellij.execution.process.ProcessListener -import com.intellij.execution.process.ProcessOutput import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile -import com.intellij.util.execution.ParametersListUtil -import org.move.cli.Consts -import org.move.cli.MoveProject -import org.move.cli.externalLinter.ExternalLinter -import org.move.cli.externalLinter.externalLinterSettings -import org.move.cli.settings.moveSettings -import org.move.openapiext.* -import org.move.openapiext.common.isUnitTestMode -import org.move.stdext.RsResult -import org.move.stdext.RsResult.Err -import org.move.stdext.RsResult.Ok -import org.move.stdext.unwrapOrElse +import org.move.openapiext.RsProcessResult import java.nio.file.Path -sealed class BlockchainCli { +abstract class BlockchainCli { abstract val cliLocation: Path @@ -38,184 +25,4 @@ sealed class BlockchainCli { owner: Disposable, processListener: ProcessListener ): RsProcessResult - - data class Aptos(override val cliLocation: Path): BlockchainCli() { - override fun init( - project: Project, - parentDisposable: Disposable, - rootDirectory: VirtualFile, - packageName: String - ): RsProcessResult { - if (!isUnitTestMode) { - checkIsBackgroundThread() - } - val commandLine = CliCommandLineArgs( - "move", - listOf( - "init", - "--name", packageName, - "--assume-yes" - ), - workingDirectory = project.rootPath - ) - commandLine - .toGeneralCommandLine(cliLocation) - .execute(parentDisposable) - .unwrapOrElse { return Err(it) } - fullyRefreshDirectory(rootDirectory) - - val manifest = - checkNotNull(rootDirectory.findChild(Consts.MANIFEST_FILE)) { "Can't find the manifest file" } - return Ok(manifest) - } - - override fun fetchPackageDependencies( - project: Project, - projectDir: Path, - skipLatest: Boolean, - owner: Disposable, - processListener: ProcessListener - ): RsProcessResult { - if (project.moveSettings.fetchAptosDeps) { - val cli = - CliCommandLineArgs( - subCommand = "move", - arguments = listOfNotNull( - "compile", - "--skip-fetch-latest-git-deps".takeIf { skipLatest } - ), - workingDirectory = projectDir - ) - // TODO: as Aptos does not yet support fetching dependencies without compiling, ignore errors here, - // TODO: still better than no call at all - cli.toGeneralCommandLine(cliLocation) - .execute(owner, listener = processListener) -// .unwrapOrElse { return Err(it) } - } - return Ok(Unit) - } - - fun checkProject( - project: Project, - owner: Disposable, - args: AptosCompileArgs - ): RsResult { -// val useClippy = args.linter == ExternalLinter.CLIPPY -// && !checkNeedInstallClippy(project, args.cargoProjectDirectory) -// val checkCommand = if (useClippy) "clippy" else "check" - val extraArguments = ParametersListUtil.parse(args.extraArguments) - val cliArgs = - CliCommandLineArgs( - "move", - buildList { -// add("--message-format=json") - add("compile") - if ("--skip-fetch-latest-git-deps" !in extraArguments) { - add("--skip-fetch-latest-git-deps") - } - if (args.isCompilerV2 && "--compiler-version" !in extraArguments) { - add("--compiler-version") - add("v2") - } - if (args.isCompilerV2 && "--language-version" !in extraArguments) { - add("--language-version") - add("2.0") - } - addAll(ParametersListUtil.parse(args.extraArguments)) - }, - args.moveProjectDirectory, - environmentVariables = EnvironmentVariablesData.create(args.envs, true) - ) - return cliArgs.execute(this.cliLocation, project, owner).ignoreExitCode() - } - - private fun CliCommandLineArgs.execute( - cliPath: Path, - project: Project, - owner: Disposable = project, - stdIn: ByteArray? = null, - listener: ProcessListener? = null - ): RsProcessResult { - return toGeneralCommandLine(cliPath).execute(owner, stdIn, listener = listener) - } - } - - data class Sui(override val cliLocation: Path): BlockchainCli() { - override fun init( - project: Project, - parentDisposable: Disposable, - rootDirectory: VirtualFile, - packageName: String - ): RsProcessResult { - if (!isUnitTestMode) { - checkIsBackgroundThread() - } - val commandLine = CliCommandLineArgs( - "move", - listOf( - "new", packageName, - "--path", "." - ), - workingDirectory = project.rootPath - ) - commandLine.toGeneralCommandLine(this.cliLocation) - .execute(parentDisposable) - .unwrapOrElse { return Err(it) } - fullyRefreshDirectory(rootDirectory) - - val manifest = - checkNotNull(rootDirectory.findChild(Consts.MANIFEST_FILE)) { "Can't find the manifest file" } - return Ok(manifest) - } - - override fun fetchPackageDependencies( - project: Project, - projectDir: Path, - skipLatest: Boolean, - owner: Disposable, - processListener: ProcessListener - ): RsProcessResult { - val cli = - CliCommandLineArgs( - subCommand = "move", - arguments = listOfNotNull( - "build", - "--fetch-deps-only", - "--skip-fetch-latest-git-deps".takeIf { skipLatest }), - workingDirectory = projectDir - ) - cli.toGeneralCommandLine(cliLocation) - .execute(owner, listener = processListener) - .unwrapOrElse { return Err(it) } - return Ok(Unit) - } - } -} - -data class AptosCompileArgs( - val linter: ExternalLinter, - val moveProjectDirectory: Path, - val extraArguments: String, - val envs: Map, - val isCompilerV2: Boolean, - val skipLatestGitDeps: Boolean, -) { - companion object { - fun forMoveProject(moveProject: MoveProject): AptosCompileArgs { - val linterSettings = moveProject.project.externalLinterSettings - val moveSettings = moveProject.project.moveSettings - return AptosCompileArgs( - linterSettings.tool, - moveProject.workingDirectory, -// moveProject.project.rustSettings.compileAllTargets, - linterSettings.additionalArguments, -// settings.channel, - linterSettings.envs, - isCompilerV2 = moveSettings.isCompilerV2, - skipLatestGitDeps = moveSettings.skipFetchLatestGitDeps - ) - } - } -} - -val MoveProject.workingDirectory: Path get() = this.currentPackage.contentRoot.pathAsPath \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/cli/runConfigurations/CliCommandLineArgs.kt b/src/main/kotlin/org/move/cli/runConfigurations/CliCommandLineArgs.kt index 5ed0ab37c..0be16981f 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/CliCommandLineArgs.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/CliCommandLineArgs.kt @@ -12,14 +12,14 @@ data class CliCommandLineArgs( val environmentVariables: EnvironmentVariablesData = EnvironmentVariablesData.DEFAULT ) { fun joinArgs(): String { - return StringUtil.join(listOfNotNull(subCommand, *arguments.toTypedArray()), " ") + return StringUtil.join(subCommand?.split(" ").orEmpty() + arguments, " ") } fun toGeneralCommandLine(cliExePath: Path): GeneralCommandLine { val generalCommandLine = GeneralCommandLine() .withExePath(cliExePath.toString()) // subcommand can be null - .withParameters(listOfNotNull(subCommand)) + .withParameters(subCommand?.split(" ").orEmpty()) .withParameters(this.arguments) .withWorkDirectory(this.workingDirectory?.toString()) .withCharset(Charsets.UTF_8) diff --git a/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt b/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt new file mode 100644 index 000000000..4280ed12d --- /dev/null +++ b/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt @@ -0,0 +1,164 @@ +package org.move.cli.runConfigurations.aptos + +import com.intellij.execution.configuration.EnvironmentVariablesData +import com.intellij.execution.process.ProcessListener +import com.intellij.execution.process.ProcessOutput +import com.intellij.openapi.Disposable +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.util.execution.ParametersListUtil +import org.move.cli.Consts +import org.move.cli.MoveProject +import org.move.cli.externalLinter.ExternalLinter +import org.move.cli.externalLinter.externalLinterSettings +import org.move.cli.runConfigurations.BlockchainCli +import org.move.cli.runConfigurations.CliCommandLineArgs +import org.move.cli.settings.moveSettings +import org.move.openapiext.* +import org.move.openapiext.common.isUnitTestMode +import org.move.stdext.RsResult +import org.move.stdext.RsResult.Err +import org.move.stdext.RsResult.Ok +import org.move.stdext.unwrapOrElse +import java.nio.file.Path + +data class Aptos(override val cliLocation: Path): BlockchainCli(), Disposable { + override fun dispose() {} + + override fun init( + project: Project, + parentDisposable: Disposable, + rootDirectory: VirtualFile, + packageName: String + ): RsProcessResult { + if (!isUnitTestMode) { + checkIsBackgroundThread() + } + val commandLine = CliCommandLineArgs( + "move", + listOf( + "init", + "--name", packageName, + "--assume-yes" + ), + workingDirectory = project.rootPath + ) + commandLine + .toGeneralCommandLine(cliLocation) + .execute(parentDisposable) + .unwrapOrElse { return Err(it) } + fullyRefreshDirectory(rootDirectory) + + val manifest = + checkNotNull(rootDirectory.findChild(Consts.MANIFEST_FILE)) { "Can't find the manifest file" } + return Ok(manifest) + } + + override fun fetchPackageDependencies( + project: Project, + projectDir: Path, + skipLatest: Boolean, + owner: Disposable, + processListener: ProcessListener + ): RsProcessResult { + if (project.moveSettings.fetchAptosDeps) { + val cli = + CliCommandLineArgs( + subCommand = "move", + arguments = listOfNotNull( + "compile", + "--skip-fetch-latest-git-deps".takeIf { skipLatest } + ), + workingDirectory = projectDir + ) + // TODO: as Aptos does not yet support fetching dependencies without compiling, ignore errors here, + // TODO: still better than no call at all + cli.toGeneralCommandLine(cliLocation) + .execute(owner, listener = processListener) +// .unwrapOrElse { return Err(it) } + } + return Ok(Unit) + } + + fun checkProject( + project: Project, + owner: Disposable, + args: AptosCompileArgs + ): RsResult { +// val useClippy = args.linter == ExternalLinter.CLIPPY +// && !checkNeedInstallClippy(project, args.cargoProjectDirectory) +// val checkCommand = if (useClippy) "clippy" else "check" + val extraArguments = ParametersListUtil.parse(args.extraArguments) + val commandLine = + CliCommandLineArgs( + "move", + buildList { +// add("--message-format=json") + add("compile") + if ("--skip-fetch-latest-git-deps" !in extraArguments) { + add("--skip-fetch-latest-git-deps") + } + if (args.isCompilerV2 && "--compiler-version" !in extraArguments) { + add("--compiler-version") + add("v2") + } + if (args.isCompilerV2 && "--language-version" !in extraArguments) { + add("--language-version") + add("2.0") + } + addAll(ParametersListUtil.parse(args.extraArguments)) + }, + args.moveProjectDirectory, + environmentVariables = EnvironmentVariablesData.create(args.envs, true) + ) + return executeCommandLine(commandLine).ignoreExitCode() + } + + fun downloadBytecode( + moveProject: MoveProject, + accountAddress: String, + packageName: String + ): RsResult { + val commandLine = CliCommandLineArgs( + subCommand = "move download", + arguments = listOf("--account", accountAddress, "--package", packageName, "--bytecode"), + workingDirectory = moveProject.workingDirectory, + environmentVariables = EnvironmentVariablesData.DEFAULT + ) + return executeCommandLine(commandLine).ignoreExitCode() + } + + private fun executeCommandLine(commandLine: CliCommandLineArgs): RsProcessResult { + return commandLine + .toGeneralCommandLine(this.cliLocation) + .execute(this, stdIn = null, listener = null) + } +} + +data class AptosCompileArgs( + val linter: ExternalLinter, + val moveProjectDirectory: Path, + val extraArguments: String, + val envs: Map, + val isCompilerV2: Boolean, + val skipLatestGitDeps: Boolean, +) { + companion object { + fun forMoveProject(moveProject: MoveProject): AptosCompileArgs { + val linterSettings = moveProject.project.externalLinterSettings + val moveSettings = moveProject.project.moveSettings + return AptosCompileArgs( + linterSettings.tool, + moveProject.workingDirectory, +// moveProject.project.rustSettings.compileAllTargets, + linterSettings.additionalArguments, +// settings.channel, + linterSettings.envs, + isCompilerV2 = moveSettings.isCompilerV2, + skipLatestGitDeps = moveSettings.skipFetchLatestGitDeps + ) + } + } +} + +val MoveProject.workingDirectory: Path get() = this.currentPackage.contentRoot.pathAsPath \ No newline at end of file diff --git a/src/main/kotlin/org/move/cli/runConfigurations/sui/Sui.kt b/src/main/kotlin/org/move/cli/runConfigurations/sui/Sui.kt new file mode 100644 index 000000000..81fd27a05 --- /dev/null +++ b/src/main/kotlin/org/move/cli/runConfigurations/sui/Sui.kt @@ -0,0 +1,66 @@ +package org.move.cli.runConfigurations.sui + +import com.intellij.execution.process.ProcessListener +import com.intellij.openapi.Disposable +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import org.move.cli.Consts +import org.move.cli.runConfigurations.BlockchainCli +import org.move.cli.runConfigurations.CliCommandLineArgs +import org.move.openapiext.* +import org.move.openapiext.common.isUnitTestMode +import org.move.stdext.RsResult.Err +import org.move.stdext.RsResult.Ok +import org.move.stdext.unwrapOrElse +import java.nio.file.Path + +data class Sui(override val cliLocation: Path): BlockchainCli() { + override fun init( + project: Project, + parentDisposable: Disposable, + rootDirectory: VirtualFile, + packageName: String + ): RsProcessResult { + if (!isUnitTestMode) { + checkIsBackgroundThread() + } + val commandLine = CliCommandLineArgs( + "move", + listOf( + "new", packageName, + "--path", "." + ), + workingDirectory = project.rootPath + ) + commandLine.toGeneralCommandLine(this.cliLocation) + .execute(parentDisposable) + .unwrapOrElse { return Err(it) } + fullyRefreshDirectory(rootDirectory) + + val manifest = + checkNotNull(rootDirectory.findChild(Consts.MANIFEST_FILE)) { "Can't find the manifest file" } + return Ok(manifest) + } + + override fun fetchPackageDependencies( + project: Project, + projectDir: Path, + skipLatest: Boolean, + owner: Disposable, + processListener: ProcessListener + ): RsProcessResult { + val cli = + CliCommandLineArgs( + subCommand = "move", + arguments = listOfNotNull( + "build", + "--fetch-deps-only", + "--skip-fetch-latest-git-deps".takeIf { skipLatest }), + workingDirectory = projectDir + ) + cli.toGeneralCommandLine(cliLocation) + .execute(owner, listener = processListener) + .unwrapOrElse { return Err(it) } + return Ok(Unit) + } +} diff --git a/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt b/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt index 3a3aaaf4a..44de2d69b 100644 --- a/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt +++ b/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt @@ -8,8 +8,8 @@ import com.intellij.openapi.components.service import com.intellij.openapi.project.Project import com.intellij.openapi.util.registry.Registry import org.move.cli.runConfigurations.BlockchainCli -import org.move.cli.runConfigurations.BlockchainCli.Aptos -import org.move.cli.runConfigurations.BlockchainCli.Sui +import org.move.cli.runConfigurations.aptos.Aptos +import org.move.cli.runConfigurations.sui.Sui import org.move.cli.settings.Blockchain.APTOS import org.move.cli.settings.Blockchain.SUI import org.move.cli.settings.MvProjectSettingsService.MoveProjectSettings diff --git a/src/main/kotlin/org/move/ide/annotator/RsExternalLinterPass.kt b/src/main/kotlin/org/move/ide/annotator/RsExternalLinterPass.kt index 5fc8146a4..058077f9b 100644 --- a/src/main/kotlin/org/move/ide/annotator/RsExternalLinterPass.kt +++ b/src/main/kotlin/org/move/ide/annotator/RsExternalLinterPass.kt @@ -32,8 +32,8 @@ import com.intellij.psi.PsiFile import com.intellij.util.ui.update.MergingUpdateQueue import com.intellij.util.ui.update.Update import org.move.cli.externalLinter.externalLinterSettings -import org.move.cli.runConfigurations.AptosCompileArgs -import org.move.cli.runConfigurations.workingDirectory +import org.move.cli.runConfigurations.aptos.AptosCompileArgs +import org.move.cli.runConfigurations.aptos.workingDirectory import org.move.cli.settings.aptosCli import org.move.ide.annotator.RsExternalLinterResult import org.move.ide.annotator.RsExternalLinterUtils diff --git a/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt b/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt index e4d847c45..df61be0f5 100644 --- a/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt +++ b/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt @@ -28,8 +28,8 @@ import org.jetbrains.annotations.Nls import org.move.cli.externalLinter.RsExternalLinterWidget import org.move.cli.externalLinter.externalLinterSettings import org.move.cli.externalLinter.parseCompilerErrors -import org.move.cli.runConfigurations.AptosCompileArgs -import org.move.cli.runConfigurations.BlockchainCli.Aptos +import org.move.cli.runConfigurations.aptos.Aptos +import org.move.cli.runConfigurations.aptos.AptosCompileArgs import org.move.ide.annotator.RsExternalLinterFilteredMessage.Companion.filterMessage import org.move.ide.annotator.RsExternalLinterUtils.TEST_MESSAGE import org.move.ide.notifications.logOrShowBalloon diff --git a/src/main/kotlin/org/move/ide/newProject/MoveProjectGenerator.kt b/src/main/kotlin/org/move/ide/newProject/MoveProjectGenerator.kt index 641be1858..28b0dbe97 100644 --- a/src/main/kotlin/org/move/ide/newProject/MoveProjectGenerator.kt +++ b/src/main/kotlin/org/move/ide/newProject/MoveProjectGenerator.kt @@ -12,7 +12,8 @@ import com.intellij.platform.DirectoryProjectGenerator import com.intellij.platform.DirectoryProjectGeneratorBase import com.intellij.platform.ProjectGeneratorPeer import org.move.cli.PluginApplicationDisposable -import org.move.cli.runConfigurations.BlockchainCli +import org.move.cli.runConfigurations.aptos.Aptos +import org.move.cli.runConfigurations.sui.Sui import org.move.cli.settings.Blockchain import org.move.cli.settings.Blockchain.APTOS import org.move.cli.settings.Blockchain.SUI @@ -53,11 +54,11 @@ class MoveProjectGenerator: DirectoryProjectGeneratorBase(), val aptosPath = AptosExecType.aptosExecPath(projectConfig.aptosExecType, projectConfig.localAptosPath) ?: error("validated before") - BlockchainCli.Aptos(aptosPath) + Aptos(aptosPath) } SUI -> { val suiPath = projectConfig.localSuiPath?.toPathOrNull() ?: error("validated before") - BlockchainCli.Sui(suiPath) + Sui(suiPath) } } val manifestFile = @@ -76,11 +77,11 @@ class MoveProjectGenerator: DirectoryProjectGeneratorBase(), project.moveSettings.modify { it.blockchain = blockchain when (projectCli) { - is BlockchainCli.Aptos -> { + is Aptos -> { it.aptosExecType = projectConfig.aptosExecType it.localAptosPath = projectConfig.localAptosPath } - is BlockchainCli.Sui -> { + is Sui -> { it.localSuiPath = projectConfig.localSuiPath } } diff --git a/src/main/kotlin/org/move/openapiext/CommandLineExt.kt b/src/main/kotlin/org/move/openapiext/CommandLineExt.kt index e79b218e0..a748d4c86 100644 --- a/src/main/kotlin/org/move/openapiext/CommandLineExt.kt +++ b/src/main/kotlin/org/move/openapiext/CommandLineExt.kt @@ -64,6 +64,7 @@ fun GeneralCommandLine.execute(): ProcessOutput? { return output } +/// `owner` parameter represents the object whose lifetime it's using for the process lifetime fun GeneralCommandLine.execute( owner: Disposable, stdIn: ByteArray? = null, @@ -72,34 +73,36 @@ fun GeneralCommandLine.execute( ): RsProcessResult { LOG.info("Executing `$commandLineString`") - val handler = MvCapturingProcessHandler.startProcess(this) // The OS process is started here + 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)) } + // kill process on dispose() val processKiller = Disposable { // Don't attempt a graceful termination, Cargo can be SIGKILLed safely. // https://github.com/rust-lang/cargo/issues/3566 - if (!handler.isProcessTerminated) { - handler.process.destroyForcibly() // Send SIGKILL - handler.destroyProcess() + if (!processHandler.isProcessTerminated) { + processHandler.process.destroyForcibly() // Send SIGKILL + processHandler.destroyProcess() } } @Suppress("DEPRECATION") - val alreadyDisposed = runReadAction { - if (Disposer.isDisposed(owner)) { - true - } else { - Disposer.register(owner, processKiller) - false + val ownerIsAlreadyDisposed = + runReadAction { + // check that owner is disposed, kill process then + if (Disposer.isDisposed(owner)) { + true + } else { + Disposer.register(owner, processKiller) + false + } } - } - - if (alreadyDisposed) { + if (ownerIsAlreadyDisposed) { Disposer.dispose(processKiller) // Kill the process - // On the one hand, this seems fishy, // on the other hand, this is isomorphic // to the scenario where cargoKiller triggers. @@ -113,14 +116,14 @@ fun GeneralCommandLine.execute( ) } - listener?.let { handler.addProcessListener(it) } + listener?.let { processHandler.addProcessListener(it) } val output = try { if (stdIn != null) { - handler.processInput.use { it.write(stdIn) } + processHandler.processInput.use { it.write(stdIn) } } - handler.runner() + processHandler.runner() } finally { Disposer.dispose(processKiller) } From abe54da10ecfbba52a3ccb62bd16273b0948de0c Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Wed, 15 May 2024 00:24:38 +0300 Subject: [PATCH 03/14] correct disposable hierarchy for blockchain cli calls --- .../org/move/cli/MoveProjectsService.kt | 2 +- .../org/move/cli/MoveProjectsSyncTask.kt | 23 +++++++++++--- .../cli/runConfigurations/BlockchainCli.kt | 24 ++++++++++++-- .../move/cli/runConfigurations/aptos/Aptos.kt | 31 ++++++------------- .../org/move/cli/runConfigurations/sui/Sui.kt | 31 ++++++++----------- .../cli/settings/MvProjectSettingsService.kt | 20 +++++++----- .../ide/annotator/RsExternalLinterPass.kt | 6 ++-- .../ide/annotator/RsExternalLinterUtils.kt | 14 +++------ .../ide/newProject/MoveProjectGenerator.kt | 5 ++- 9 files changed, 83 insertions(+), 73 deletions(-) diff --git a/src/main/kotlin/org/move/cli/MoveProjectsService.kt b/src/main/kotlin/org/move/cli/MoveProjectsService.kt index 374d04a2a..fbfa252be 100644 --- a/src/main/kotlin/org/move/cli/MoveProjectsService.kt +++ b/src/main/kotlin/org/move/cli/MoveProjectsService.kt @@ -141,7 +141,7 @@ class MoveProjectsService(val project: Project): Disposable { private fun doRefreshProjects(project: Project, reason: String?): CompletableFuture> { val moveProjectsFut = CompletableFuture>() - val syncTask = MoveProjectsSyncTask(project, moveProjectsFut, reason) + val syncTask = MoveProjectsSyncTask(project, this, moveProjectsFut, reason) project.taskQueue.run(syncTask) return moveProjectsFut.thenApply { updatedProjects -> diff --git a/src/main/kotlin/org/move/cli/MoveProjectsSyncTask.kt b/src/main/kotlin/org/move/cli/MoveProjectsSyncTask.kt index 643cacb6b..61c1dac80 100644 --- a/src/main/kotlin/org/move/cli/MoveProjectsSyncTask.kt +++ b/src/main/kotlin/org/move/cli/MoveProjectsSyncTask.kt @@ -13,6 +13,7 @@ import com.intellij.build.progress.BuildProgressDescriptor import com.intellij.execution.process.ProcessAdapter import com.intellij.execution.process.ProcessEvent import com.intellij.icons.AllIcons +import com.intellij.openapi.Disposable import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.application.runReadAction @@ -22,13 +23,14 @@ import com.intellij.openapi.progress.ProgressIndicator import com.intellij.openapi.progress.Task import com.intellij.openapi.project.DumbAwareAction import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Key import com.intellij.openapi.util.NlsContexts import com.intellij.openapi.vfs.VirtualFile import org.move.cli.MoveProject.UpdateStatus import org.move.cli.manifest.MoveToml import org.move.cli.settings.blockchain -import org.move.cli.settings.blockchainCli +import org.move.cli.settings.getBlockchainCli import org.move.cli.settings.moveSettings import org.move.lang.toNioPathOrNull import org.move.lang.toTomlFile @@ -42,9 +44,20 @@ import javax.swing.JComponent class MoveProjectsSyncTask( project: Project, + parentDisposable: Disposable, private val future: CompletableFuture>, private val reason: String? -): Task.Backgroundable(project, "Reloading Move packages", true) { +): Task.Backgroundable(project, "Reloading Move packages", true), Disposable { + + init { + Disposer.register(parentDisposable, this) + } + + override fun dispose() {} + + override fun onCancel() { + Disposer.dispose(this) + } override fun run(indicator: ProgressIndicator) { indicator.isIndeterminate = true @@ -66,7 +79,6 @@ class MoveProjectsSyncTask( } else { syncProgress.finish() } - refreshedProjects } catch (e: Throwable) { if (e is ProcessCanceledException) { @@ -81,6 +93,8 @@ class MoveProjectsSyncTask( val elapsed = System.currentTimeMillis() - before LOG.logProjectsRefresh("finished Move projects Sync Task in $elapsed ms", reason) + + Disposer.dispose(this) } private fun doRun( @@ -160,7 +174,7 @@ class MoveProjectsSyncTask( val blockchain = project.blockchain val skipLatest = project.moveSettings.skipFetchLatestGitDeps - val blockchainCli = project.blockchainCli + val blockchainCli = project.getBlockchainCli(parentDisposable = this) when { blockchainCli == null -> TaskResult.Err("Invalid $blockchain CLI configuration") else -> { @@ -169,7 +183,6 @@ class MoveProjectsSyncTask( project, projectRoot, skipLatest, - owner = project.rootDisposable, processListener = listener ).unwrapOrElse { return@runWithChildProgress TaskResult.Err( diff --git a/src/main/kotlin/org/move/cli/runConfigurations/BlockchainCli.kt b/src/main/kotlin/org/move/cli/runConfigurations/BlockchainCli.kt index 975086868..d888df2ad 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/BlockchainCli.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/BlockchainCli.kt @@ -1,19 +1,29 @@ package org.move.cli.runConfigurations import com.intellij.execution.process.ProcessListener +import com.intellij.execution.process.ProcessOutput import com.intellij.openapi.Disposable import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer import com.intellij.openapi.vfs.VirtualFile import org.move.openapiext.RsProcessResult +import org.move.openapiext.execute import java.nio.file.Path -abstract class BlockchainCli { +abstract class BlockchainCli(parentDisposable: Disposable?): Disposable { + + init { + if (parentDisposable != null) { + Disposer.register(parentDisposable, this) + } + } + + override fun dispose() {} abstract val cliLocation: Path abstract fun init( project: Project, - parentDisposable: Disposable, rootDirectory: VirtualFile, packageName: String, ): RsProcessResult @@ -22,7 +32,15 @@ abstract class BlockchainCli { project: Project, projectDir: Path, skipLatest: Boolean, - owner: Disposable, processListener: ProcessListener ): RsProcessResult + + protected fun executeCommandLine( + commandLine: CliCommandLineArgs, + listener: ProcessListener? = null + ): RsProcessResult { + return commandLine + .toGeneralCommandLine(this.cliLocation) + .execute(this, stdIn = null, listener = listener) + } } \ No newline at end of file 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 4280ed12d..77bb6f305 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt @@ -22,12 +22,13 @@ import org.move.stdext.RsResult.Ok import org.move.stdext.unwrapOrElse import java.nio.file.Path -data class Aptos(override val cliLocation: Path): BlockchainCli(), Disposable { - override fun dispose() {} +data class Aptos( + override val cliLocation: Path, + val parentDisposable: Disposable? +): BlockchainCli(parentDisposable) { override fun init( project: Project, - parentDisposable: Disposable, rootDirectory: VirtualFile, packageName: String ): RsProcessResult { @@ -43,10 +44,8 @@ data class Aptos(override val cliLocation: Path): BlockchainCli(), Disposable { ), workingDirectory = project.rootPath ) - commandLine - .toGeneralCommandLine(cliLocation) - .execute(parentDisposable) - .unwrapOrElse { return Err(it) } + executeCommandLine(commandLine).unwrapOrElse { return Err(it) } + fullyRefreshDirectory(rootDirectory) val manifest = @@ -58,11 +57,10 @@ data class Aptos(override val cliLocation: Path): BlockchainCli(), Disposable { project: Project, projectDir: Path, skipLatest: Boolean, - owner: Disposable, processListener: ProcessListener ): RsProcessResult { if (project.moveSettings.fetchAptosDeps) { - val cli = + val commandLine = CliCommandLineArgs( subCommand = "move", arguments = listOfNotNull( @@ -73,18 +71,13 @@ data class Aptos(override val cliLocation: Path): BlockchainCli(), Disposable { ) // TODO: as Aptos does not yet support fetching dependencies without compiling, ignore errors here, // TODO: still better than no call at all - cli.toGeneralCommandLine(cliLocation) - .execute(owner, listener = processListener) + executeCommandLine(commandLine, listener = processListener) // .unwrapOrElse { return Err(it) } } return Ok(Unit) } - fun checkProject( - project: Project, - owner: Disposable, - args: AptosCompileArgs - ): RsResult { + fun checkProject(args: AptosCompileArgs): RsResult { // val useClippy = args.linter == ExternalLinter.CLIPPY // && !checkNeedInstallClippy(project, args.cargoProjectDirectory) // val checkCommand = if (useClippy) "clippy" else "check" @@ -127,12 +120,6 @@ data class Aptos(override val cliLocation: Path): BlockchainCli(), Disposable { ) return executeCommandLine(commandLine).ignoreExitCode() } - - private fun executeCommandLine(commandLine: CliCommandLineArgs): RsProcessResult { - return commandLine - .toGeneralCommandLine(this.cliLocation) - .execute(this, stdIn = null, listener = null) - } } data class AptosCompileArgs( diff --git a/src/main/kotlin/org/move/cli/runConfigurations/sui/Sui.kt b/src/main/kotlin/org/move/cli/runConfigurations/sui/Sui.kt index 81fd27a05..899de681e 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/sui/Sui.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/sui/Sui.kt @@ -14,27 +14,25 @@ import org.move.stdext.RsResult.Ok import org.move.stdext.unwrapOrElse import java.nio.file.Path -data class Sui(override val cliLocation: Path): BlockchainCli() { +data class Sui(override val cliLocation: Path, val parentDisposable: Disposable?): + BlockchainCli(parentDisposable) { override fun init( project: Project, - parentDisposable: Disposable, rootDirectory: VirtualFile, packageName: String ): RsProcessResult { if (!isUnitTestMode) { checkIsBackgroundThread() } - val commandLine = CliCommandLineArgs( - "move", - listOf( - "new", packageName, - "--path", "." - ), - workingDirectory = project.rootPath - ) - commandLine.toGeneralCommandLine(this.cliLocation) - .execute(parentDisposable) + val commandLine = + CliCommandLineArgs( + "move new", + arguments = listOf("--path", "."), + workingDirectory = project.rootPath + ) + executeCommandLine(commandLine) .unwrapOrElse { return Err(it) } + fullyRefreshDirectory(rootDirectory) val manifest = @@ -46,20 +44,17 @@ data class Sui(override val cliLocation: Path): BlockchainCli() { project: Project, projectDir: Path, skipLatest: Boolean, - owner: Disposable, processListener: ProcessListener ): RsProcessResult { - val cli = + val commandLine = CliCommandLineArgs( - subCommand = "move", + subCommand = "move build", arguments = listOfNotNull( - "build", "--fetch-deps-only", "--skip-fetch-latest-git-deps".takeIf { skipLatest }), workingDirectory = projectDir ) - cli.toGeneralCommandLine(cliLocation) - .execute(owner, listener = processListener) + executeCommandLine(commandLine, listener = processListener) .unwrapOrElse { return Err(it) } return Ok(Unit) } diff --git a/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt b/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt index 44de2d69b..c9c6504a3 100644 --- a/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt +++ b/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt @@ -1,6 +1,7 @@ package org.move.cli.settings import com.intellij.execution.configurations.PathEnvironmentVariableUtil +import com.intellij.openapi.Disposable import com.intellij.openapi.components.State import com.intellij.openapi.components.Storage import com.intellij.openapi.components.StoragePathMacros @@ -119,7 +120,7 @@ class MvProjectSettingsService( val Project.blockchain: Blockchain get() = this.moveSettings.blockchain -fun Project.getBlockchainCli(blockchain: Blockchain): BlockchainCli? { +fun Project.getBlockchainCli(blockchain: Blockchain, parentDisposable: Disposable? = null): BlockchainCli? { return when (blockchain) { APTOS -> { val aptosExecPath = @@ -127,21 +128,24 @@ fun Project.getBlockchainCli(blockchain: Blockchain): BlockchainCli? { this.moveSettings.aptosExecType, this.moveSettings.localAptosPath ) - aptosExecPath?.let { Aptos(it) } + aptosExecPath?.let { Aptos(it, parentDisposable) } } - SUI -> this.moveSettings.localSuiPath?.toPathOrNull()?.let { Sui(it) } + SUI -> this.moveSettings.localSuiPath?.toPathOrNull()?.let { Sui(it, parentDisposable) } } } -val Project.blockchainCli: BlockchainCli? get() = getBlockchainCli(this.blockchain) +fun Project.getBlockchainCli(parentDisposable: Disposable?): BlockchainCli? = + getBlockchainCli(this.blockchain, parentDisposable) -val Project.aptosCli: Aptos? get() = getBlockchainCli(APTOS) as? Aptos +fun Project.getAptosCli(parentDisposable: Disposable? = null): Aptos? = + getBlockchainCli(APTOS, parentDisposable) as? Aptos -val Project.suiCli: Sui? get() = getBlockchainCli(SUI) as? Sui +fun Project.getSuiCli(parentDisposable: Disposable? = null): Sui? = + getBlockchainCli(SUI, parentDisposable) as? Sui -val Project.aptosExecPath: Path? get() = this.aptosCli?.cliLocation +val Project.aptosExecPath: Path? get() = this.getAptosCli()?.cliLocation -val Project.suiExecPath: Path? get() = this.suiCli?.cliLocation +val Project.suiExecPath: Path? get() = this.getSuiCli()?.cliLocation fun Path?.isValidExecutable(): Boolean { return this != null diff --git a/src/main/kotlin/org/move/ide/annotator/RsExternalLinterPass.kt b/src/main/kotlin/org/move/ide/annotator/RsExternalLinterPass.kt index 058077f9b..784137c95 100644 --- a/src/main/kotlin/org/move/ide/annotator/RsExternalLinterPass.kt +++ b/src/main/kotlin/org/move/ide/annotator/RsExternalLinterPass.kt @@ -34,7 +34,7 @@ import com.intellij.util.ui.update.Update import org.move.cli.externalLinter.externalLinterSettings import org.move.cli.runConfigurations.aptos.AptosCompileArgs import org.move.cli.runConfigurations.aptos.workingDirectory -import org.move.cli.settings.aptosCli +import org.move.cli.settings.getAptosCli import org.move.ide.annotator.RsExternalLinterResult import org.move.ide.annotator.RsExternalLinterUtils import org.move.ide.annotator.addHighlightsForFile @@ -69,11 +69,11 @@ class RsExternalLinterPass( disposable = myProject.messageBus.createDisposableOnAnyPsiChange() .also { Disposer.register(moduleOrProject, it) } + val aptos = myProject.getAptosCli(parentDisposable = disposable) ?: return val args = AptosCompileArgs.forMoveProject(moveProject) annotationInfo = RsExternalLinterUtils.checkLazily( - myProject.aptosCli ?: return, + aptos, myProject, - disposable, moveProject.workingDirectory, args ) diff --git a/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt b/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt index df61be0f5..5b147bb10 100644 --- a/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt +++ b/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt @@ -60,9 +60,7 @@ object RsExternalLinterUtils { */ fun checkLazily( aptosCli: Aptos, -// toolchain: RsToolchainBase, project: Project, - owner: Disposable, workingDirectory: Path, args: AptosCompileArgs ): Lazy { @@ -86,7 +84,7 @@ object RsExternalLinterUtils { lazy { // This code will be executed out of read action in background thread if (!isUnitTestMode) checkReadAccessNotAllowed() - checkWrapped(aptosCli, project, owner, workingDirectory, args) + checkWrapped(aptosCli, project, args) } } } @@ -94,8 +92,6 @@ object RsExternalLinterUtils { private fun checkWrapped( aptosCli: Aptos, project: Project, - owner: Disposable, - workingDirectory: Path, args: AptosCompileArgs ): RsExternalLinterResult? { val widget = WriteAction.computeAndWait { @@ -110,7 +106,7 @@ object RsExternalLinterUtils { override fun run(indicator: ProgressIndicator) { widget?.inProgress = true - future.complete(check(aptosCli, project, owner, args)) + future.complete(check(aptosCli, args)) } override fun onFinished() { @@ -123,14 +119,12 @@ object RsExternalLinterUtils { private fun check( aptosCli: Aptos, - project: Project, - owner: Disposable, - args: AptosCompileArgs + aptosCompileArgs: AptosCompileArgs ): RsExternalLinterResult? { ProgressManager.checkCanceled() val started = Instant.now() val output = aptosCli - .checkProject(project, owner, args) + .checkProject(aptosCompileArgs) .unwrapOrElse { e -> LOG.error(e) return null diff --git a/src/main/kotlin/org/move/ide/newProject/MoveProjectGenerator.kt b/src/main/kotlin/org/move/ide/newProject/MoveProjectGenerator.kt index 28b0dbe97..dbae014d4 100644 --- a/src/main/kotlin/org/move/ide/newProject/MoveProjectGenerator.kt +++ b/src/main/kotlin/org/move/ide/newProject/MoveProjectGenerator.kt @@ -54,11 +54,11 @@ class MoveProjectGenerator: DirectoryProjectGeneratorBase(), val aptosPath = AptosExecType.aptosExecPath(projectConfig.aptosExecType, projectConfig.localAptosPath) ?: error("validated before") - Aptos(aptosPath) + Aptos(aptosPath, disposable) } SUI -> { val suiPath = projectConfig.localSuiPath?.toPathOrNull() ?: error("validated before") - Sui(suiPath) + Sui(suiPath, disposable) } } val manifestFile = @@ -66,7 +66,6 @@ class MoveProjectGenerator: DirectoryProjectGeneratorBase(), val manifestFile = projectCli.init( project, - disposable, rootDirectory = baseDir, packageName = packageName ) From 85e116d718afb5079550f04ef7511bb578e075d8 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Wed, 15 May 2024 00:40:11 +0300 Subject: [PATCH 04/14] decompiler commands --- .../move/cli/runConfigurations/aptos/Aptos.kt | 27 ++++++++++++++++--- src/main/kotlin/org/move/openapiext/utils.kt | 3 +++ 2 files changed, 27 insertions(+), 3 deletions(-) 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 77bb6f305..4f4712a97 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt @@ -21,6 +21,7 @@ import org.move.stdext.RsResult.Err import org.move.stdext.RsResult.Ok import org.move.stdext.unwrapOrElse import java.nio.file.Path +import java.nio.file.Paths data class Aptos( override val cliLocation: Path, @@ -84,10 +85,9 @@ data class Aptos( val extraArguments = ParametersListUtil.parse(args.extraArguments) val commandLine = CliCommandLineArgs( - "move", + "move compile", buildList { // add("--message-format=json") - add("compile") if ("--skip-fetch-latest-git-deps" !in extraArguments) { add("--skip-fetch-latest-git-deps") } @@ -107,7 +107,7 @@ data class Aptos( return executeCommandLine(commandLine).ignoreExitCode() } - fun downloadBytecode( + fun downloadPackage( moveProject: MoveProject, accountAddress: String, packageName: String @@ -120,6 +120,27 @@ data class Aptos( ) return executeCommandLine(commandLine).ignoreExitCode() } + + fun decompileDownloadedPackage(downloadedPackagePath: Path): RsProcessResult { + val bytecodeModulesPath = + downloadedPackagePath.resolve("bytecode_modules").toAbsolutePath().toString() + val commandLine = CliCommandLineArgs( + subCommand = "move decompile", + arguments = listOf("--package-path", bytecodeModulesPath), + workingDirectory = downloadedPackagePath + ) + return executeCommandLine(commandLine) + } + + fun decompileFile(bytecodeFilePath: String): RsProcessResult { + val fileRoot = Paths.get(bytecodeFilePath).parent + val commandLine = CliCommandLineArgs( + subCommand = "move decompile", + arguments = listOf("--bytecode-path", bytecodeFilePath), + workingDirectory = fileRoot + ) + return executeCommandLine(commandLine) + } } data class AptosCompileArgs( diff --git a/src/main/kotlin/org/move/openapiext/utils.kt b/src/main/kotlin/org/move/openapiext/utils.kt index 9bf433bbc..16b6d5c0e 100644 --- a/src/main/kotlin/org/move/openapiext/utils.kt +++ b/src/main/kotlin/org/move/openapiext/utils.kt @@ -239,4 +239,7 @@ inline fun getElements( StubIndex.getElements(indexKey, key, project, scope, Psi::class.java) fun joinPath(segments: Array) = + segments.joinTo(StringBuilder(), Platform.current().fileSeparator.toString()).toString() + +fun joinPath(vararg segments: String) = segments.joinTo(StringBuilder(), Platform.current().fileSeparator.toString()).toString() \ No newline at end of file From 0af4a38449b84d1598949568b890c348e1e6a44b Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Wed, 15 May 2024 00:56:24 +0300 Subject: [PATCH 05/14] fix some deprecations --- src/main/kotlin/org/move/cli/MoveProjectsService.kt | 10 +++++----- src/main/kotlin/org/move/openapiext/utils.kt | 2 +- .../kotlin/org/move/utils/MvBackgroundTaskQueue.kt | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/org/move/cli/MoveProjectsService.kt b/src/main/kotlin/org/move/cli/MoveProjectsService.kt index fbfa252be..163528c0e 100644 --- a/src/main/kotlin/org/move/cli/MoveProjectsService.kt +++ b/src/main/kotlin/org/move/cli/MoveProjectsService.kt @@ -12,6 +12,7 @@ import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTrack import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.project.Project +import com.intellij.openapi.project.RootsChangeRescanningInfo import com.intellij.openapi.project.ex.ProjectEx import com.intellij.openapi.roots.ModuleRootModificationUtil import com.intellij.openapi.roots.ProjectFileIndex @@ -219,11 +220,10 @@ class MoveProjectsService(val project: Project): Disposable { // disable for unit-tests: in those cases roots change is done by the test framework runOnlyInNonLightProject(project) { ProjectRootManagerEx.getInstanceEx(project) - .makeRootsChange(EmptyRunnable.getInstance(), false, true) -// .makeRootsChange( -// EmptyRunnable.getInstance(), -// RootsChangeRescanningInfo.TOTAL_RESCAN -// ) + .makeRootsChange( + EmptyRunnable.getInstance(), + RootsChangeRescanningInfo.TOTAL_RESCAN + ) } // increments structure modification counter in the subscriber project.messageBus diff --git a/src/main/kotlin/org/move/openapiext/utils.kt b/src/main/kotlin/org/move/openapiext/utils.kt index 16b6d5c0e..74ef282cb 100644 --- a/src/main/kotlin/org/move/openapiext/utils.kt +++ b/src/main/kotlin/org/move/openapiext/utils.kt @@ -238,7 +238,7 @@ inline fun getElements( ): Collection = StubIndex.getElements(indexKey, key, project, scope, Psi::class.java) -fun joinPath(segments: Array) = +fun joinPathArray(segments: Array) = segments.joinTo(StringBuilder(), Platform.current().fileSeparator.toString()).toString() fun joinPath(vararg segments: String) = diff --git a/src/main/kotlin/org/move/utils/MvBackgroundTaskQueue.kt b/src/main/kotlin/org/move/utils/MvBackgroundTaskQueue.kt index 3d85e652a..d2a6fe485 100644 --- a/src/main/kotlin/org/move/utils/MvBackgroundTaskQueue.kt +++ b/src/main/kotlin/org/move/utils/MvBackgroundTaskQueue.kt @@ -141,7 +141,7 @@ class MvBackgroundTaskQueue { onFinish(this) continuation.run() }, - ModalityState.NON_MODAL + ModalityState.nonModal() ) } From 7947d59aa14ab4c2cdfe15fa502e8abb39976c34 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Thu, 16 May 2024 14:33:07 +0300 Subject: [PATCH 06/14] external linter inspection, action and button in the Aptos tool window --- .../org/move/cli/runConfigurations/Utils.kt | 18 +- .../aptos/RunAptosCommandActionBase.kt | 19 ++ .../org/move/cli/sdks/DownloadAptosSdkTask.kt | 2 - .../ide/actions/MoveEditSettingsAction.kt | 3 +- .../ide/actions/RsRunExternalLinterAction.kt | 39 ++++ .../ide/annotator/RsExternalLinterUtils.kt | 1 - .../inspections/MvExternalLinterInspection.kt | 173 ++++++++++++++++++ .../resources/META-INF/intellij-move-core.xml | 17 +- src/main/resources/icons/externalLinter.svg | 26 +++ .../resources/icons/externalLinter_dark.svg | 26 +++ .../MvExternalLinter.html | 5 + 11 files changed, 323 insertions(+), 6 deletions(-) create mode 100644 src/main/kotlin/org/move/cli/runConfigurations/aptos/RunAptosCommandActionBase.kt create mode 100644 src/main/kotlin/org/move/ide/actions/RsRunExternalLinterAction.kt create mode 100644 src/main/kotlin/org/move/ide/inspections/MvExternalLinterInspection.kt create mode 100644 src/main/resources/icons/externalLinter.svg create mode 100644 src/main/resources/icons/externalLinter_dark.svg create mode 100644 src/main/resources/inspectionDescriptions/MvExternalLinter.html diff --git a/src/main/kotlin/org/move/cli/runConfigurations/Utils.kt b/src/main/kotlin/org/move/cli/runConfigurations/Utils.kt index 37568b8d4..564aab0f6 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/Utils.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/Utils.kt @@ -1,6 +1,22 @@ package org.move.cli.runConfigurations +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.actionSystem.DataContext import com.intellij.openapi.project.Project +import org.move.cli.MoveProject import org.move.cli.moveProjectsService -val Project.hasMoveProject: Boolean get() = moveProjectsService.allProjects.isNotEmpty() \ No newline at end of file +val Project.hasMoveProject: Boolean get() = moveProjectsService.allProjects.isNotEmpty() + +fun getAppropriateMoveProject(dataContext: DataContext): MoveProject? { + val moveProjectsService = dataContext.getData(CommonDataKeys.PROJECT)?.moveProjectsService ?: return null + moveProjectsService.allProjects.singleOrNull()?.let { return it } + + dataContext.getData(CommonDataKeys.VIRTUAL_FILE) + ?.let { moveProjectsService.findMoveProjectForFile(it) } + ?.let { return it } + +// return dataContext.getData(AptosToolWindow.SELECTED_CARGO_PROJECT) +// ?: moveProjectsService.allProjects.firstOrNull() + return moveProjectsService.allProjects.firstOrNull() +} diff --git a/src/main/kotlin/org/move/cli/runConfigurations/aptos/RunAptosCommandActionBase.kt b/src/main/kotlin/org/move/cli/runConfigurations/aptos/RunAptosCommandActionBase.kt new file mode 100644 index 000000000..f9d11ddeb --- /dev/null +++ b/src/main/kotlin/org/move/cli/runConfigurations/aptos/RunAptosCommandActionBase.kt @@ -0,0 +1,19 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.move.cli.runConfigurations.aptos + +import com.intellij.openapi.actionSystem.ActionUpdateThread +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.project.DumbAwareAction +import org.move.cli.runConfigurations.hasMoveProject + +abstract class RunAptosCommandActionBase(text: String? = null) : DumbAwareAction(text) { + override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT + override fun update(e: AnActionEvent) { + val hasMoveProject = e.project?.hasMoveProject == true + e.presentation.isEnabledAndVisible = hasMoveProject + } +} diff --git a/src/main/kotlin/org/move/cli/sdks/DownloadAptosSdkTask.kt b/src/main/kotlin/org/move/cli/sdks/DownloadAptosSdkTask.kt index 994a729d8..bc5d60173 100644 --- a/src/main/kotlin/org/move/cli/sdks/DownloadAptosSdkTask.kt +++ b/src/main/kotlin/org/move/cli/sdks/DownloadAptosSdkTask.kt @@ -43,7 +43,6 @@ class DownloadAptosSdkTask( ) val (file, _) = downloader.download(tmpDownloadDir).first() tmpDownloadFile = file - println("tmpDownloadFile = $tmpDownloadFile") } catch (e: IOException) { throw RuntimeException( "Failed to download $archiveFileName from $url. ${e.message}", @@ -55,7 +54,6 @@ class DownloadAptosSdkTask( indicator.text = "Installing Aptos SDK..." indicator.text2 = "Unpacking $archiveFileName" - println("Unpacking $archiveFileName into $tmpExtractionDir") try { tmpExtractionDir.mkdir() Decompressor.Zip(tmpDownloadFile).withZipExtensions() diff --git a/src/main/kotlin/org/move/ide/actions/MoveEditSettingsAction.kt b/src/main/kotlin/org/move/ide/actions/MoveEditSettingsAction.kt index b89e5bd68..c23926a46 100644 --- a/src/main/kotlin/org/move/ide/actions/MoveEditSettingsAction.kt +++ b/src/main/kotlin/org/move/ide/actions/MoveEditSettingsAction.kt @@ -4,10 +4,11 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.project.DumbAware +import com.intellij.openapi.project.DumbAwareAction import org.move.cli.settings.PerProjectMoveConfigurable import org.move.openapiext.showSettingsDialog -class MoveEditSettingsAction : AnAction(), DumbAware { +class MoveEditSettingsAction : DumbAwareAction("Aptos Settings") { override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT diff --git a/src/main/kotlin/org/move/ide/actions/RsRunExternalLinterAction.kt b/src/main/kotlin/org/move/ide/actions/RsRunExternalLinterAction.kt new file mode 100644 index 000000000..42f3da0fe --- /dev/null +++ b/src/main/kotlin/org/move/ide/actions/RsRunExternalLinterAction.kt @@ -0,0 +1,39 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.move.ide.actions + +import com.intellij.analysis.AnalysisScope +import com.intellij.codeInspection.InspectionManager +import com.intellij.codeInspection.actions.RunInspectionIntention +import com.intellij.codeInspection.ex.InspectionManagerEx +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.util.Key +import com.intellij.profile.codeInspection.InspectionProjectProfileManager +import org.move.cli.MoveProject +import org.move.cli.runConfigurations.aptos.RunAptosCommandActionBase +import org.move.cli.runConfigurations.getAppropriateMoveProject +import org.move.ide.inspections.MvExternalLinterInspection + +class MvRunExternalLinterAction: RunAptosCommandActionBase("Run External Linter") { + override fun actionPerformed(e: AnActionEvent) { + val project = e.project ?: return + + val currentProfile = InspectionProjectProfileManager.getInstance(project).currentProfile + val wrapper = currentProfile.getInspectionTool(MvExternalLinterInspection.SHORT_NAME, project) ?: return + val managerEx = InspectionManager.getInstance(project) as InspectionManagerEx + val inspectionContext = RunInspectionIntention.createContext(wrapper, managerEx, null) + + val cargoProject = getAppropriateMoveProject(e.dataContext) + inspectionContext.putUserData(CARGO_PROJECT, cargoProject) + + inspectionContext.doInspections(AnalysisScope(project)) + } + + companion object { + @JvmField + val CARGO_PROJECT: Key = Key.create("Move project") + } +} diff --git a/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt b/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt index 5b147bb10..5de002f48 100644 --- a/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt +++ b/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt @@ -239,7 +239,6 @@ private data class RsExternalLinterFilteredMessage( message: AptosCompilerMessage, skipErrorsKnownToIde: Boolean, ): RsExternalLinterFilteredMessage? { - println(message) // if (message.message.startsWith("aborting due to") || message.message.startsWith("cannot continue")) { // return null // } diff --git a/src/main/kotlin/org/move/ide/inspections/MvExternalLinterInspection.kt b/src/main/kotlin/org/move/ide/inspections/MvExternalLinterInspection.kt new file mode 100644 index 000000000..6a614307b --- /dev/null +++ b/src/main/kotlin/org/move/ide/inspections/MvExternalLinterInspection.kt @@ -0,0 +1,173 @@ +/* + * Use of this source code is governed by the MIT license that can be + * found in the LICENSE file. + */ + +package org.move.ide.inspections + +import com.intellij.codeInsight.daemon.impl.HighlightInfo +import com.intellij.codeInspection.* +import com.intellij.codeInspection.ex.GlobalInspectionContextImpl +import com.intellij.codeInspection.ex.GlobalInspectionContextUtil +import com.intellij.codeInspection.reference.RefElement +import com.intellij.codeInspection.ui.InspectionToolPresentation +import com.intellij.openapi.Disposable +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.runReadAction +import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.Key +import com.intellij.profile.codeInspection.InspectionProjectProfileManager +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.util.containers.ContainerUtil +import org.move.cli.MoveProject +import org.move.cli.moveProjectsService +import org.move.cli.runConfigurations.aptos.AptosCompileArgs +import org.move.cli.runConfigurations.aptos.workingDirectory +import org.move.cli.settings.getAptosCli +import org.move.ide.annotator.RsExternalLinterResult +import org.move.ide.annotator.RsExternalLinterUtils +import org.move.ide.annotator.addHighlightsForFile +import org.move.ide.annotator.createDisposableOnAnyPsiChange +import org.move.lang.MoveFile +import org.move.lang.core.psi.ext.ancestorOrSelf +import org.move.lang.moveProject + +class MvExternalLinterInspection: GlobalSimpleInspectionTool() { + + override fun inspectionStarted( + manager: InspectionManager, + globalContext: GlobalInspectionContext, + problemDescriptionsProcessor: ProblemDescriptionsProcessor + ) { + globalContext.putUserData(ANALYZED_FILES, ContainerUtil.newConcurrentSet()) + } + + override fun checkFile( + file: PsiFile, + manager: InspectionManager, + problemsHolder: ProblemsHolder, + globalContext: GlobalInspectionContext, + problemDescriptionsProcessor: ProblemDescriptionsProcessor + ) { +// if (file !is MoveFile || file.containingCrate.asNotFake?.origin != PackageOrigin.WORKSPACE) return + if (file !is MoveFile) return + val analyzedFiles = globalContext.getUserData(ANALYZED_FILES) ?: return + analyzedFiles.add(file) + } + + override fun inspectionFinished( + manager: InspectionManager, + globalContext: GlobalInspectionContext, + problemDescriptionsProcessor: ProblemDescriptionsProcessor + ) { + if (globalContext !is GlobalInspectionContextImpl) return + val analyzedFiles = globalContext.getUserData(ANALYZED_FILES) ?: return + + val project = manager.project + val currentProfile = InspectionProjectProfileManager.getInstance(project).currentProfile + val toolWrapper = currentProfile.getInspectionTool(SHORT_NAME, project) ?: return + + while (true) { + val disposable = project.messageBus.createDisposableOnAnyPsiChange() + .also { Disposer.register(project, it) } + val moveProjects = run { + val allProjects = project.moveProjectsService.allProjects + if (allProjects.size == 1) { + setOf(allProjects.first()) + } else { + runReadAction { + analyzedFiles.mapNotNull { it.moveProject }.toSet() + } + } + } + val futures = moveProjects.map { + ApplicationManager.getApplication().executeOnPooledThread { + checkProjectLazily(it, disposable)?.value + } + } + val annotationResults = futures.mapNotNull { it.get() } + + val exit = runReadAction { + ProgressManager.checkCanceled() + if (Disposer.isDisposed(disposable)) return@runReadAction false + if (annotationResults.size < moveProjects.size) return@runReadAction true + for (annotationResult in annotationResults) { + val problemDescriptors = getProblemDescriptors(analyzedFiles, annotationResult) + val presentation = globalContext.getPresentation(toolWrapper) + presentation.addProblemDescriptors(problemDescriptors, globalContext) + } + true + } + + if (exit) break + } + } + + override fun getDisplayName(): String = "External linter" + + override fun getShortName(): String = SHORT_NAME + + companion object { + const val SHORT_NAME: String = "MvExternalLinter" + + private val ANALYZED_FILES: Key> = Key.create("ANALYZED_FILES") + + private fun checkProjectLazily( + moveProject: MoveProject, + disposable: Disposable + ): Lazy? = runReadAction { + val project = moveProject.project + val aptosCli = project.getAptosCli(disposable) ?: return@runReadAction null + RsExternalLinterUtils.checkLazily( + aptosCli, + project, + moveProject.workingDirectory, + AptosCompileArgs.forMoveProject(moveProject) + ) + } + + private fun getProblemDescriptors( + analyzedFiles: Set, + annotationResult: RsExternalLinterResult + ): List = buildList { + for (file in analyzedFiles) { + if (!file.isValid) continue + val highlights = mutableListOf() + highlights.addHighlightsForFile(file, annotationResult) + highlights.mapNotNull { ProblemDescriptorUtil.toProblemDescriptor(file, it) }.forEach(::add) + } + } + + private fun InspectionToolPresentation.addProblemDescriptors( + descriptors: List, + context: GlobalInspectionContext + ) { + if (descriptors.isEmpty()) return + val problems = hashMapOf>() + + for (descriptor in descriptors) { + val element = descriptor.psiElement ?: continue + val refElement = getProblemElement(element, context) ?: continue + val elementProblems = problems.getOrPut(refElement) { mutableListOf() } + elementProblems.add(descriptor) + } + + for ((refElement, problemDescriptors) in problems) { + val descriptions = problemDescriptors.toTypedArray() + addProblemElement(refElement, false, *descriptions) + } + } + + private fun getProblemElement(element: PsiElement, context: GlobalInspectionContext): RefElement? { + val problemElement = element.ancestorOrSelf() + val refElement = context.refManager.getReference(problemElement) + return if (refElement == null && problemElement != null) { + GlobalInspectionContextUtil.retrieveRefElement(element, context) + } else { + refElement + } + } + } +} diff --git a/src/main/resources/META-INF/intellij-move-core.xml b/src/main/resources/META-INF/intellij-move-core.xml index 563d79f73..8e0e6ab3a 100644 --- a/src/main/resources/META-INF/intellij-move-core.xml +++ b/src/main/resources/META-INF/intellij-move-core.xml @@ -276,6 +276,12 @@ enabledByDefault="true" level="WEAK WARNING" implementationClass="org.move.ide.inspections.ReplaceWithMethodCallInspection" /> + + + @@ -414,7 +420,11 @@ description="Download aptos-cli executable from Github" class="org.move.ide.actions.DownloadAptosSDKAction" icon="AllIcons.Actions.Download" /> - + + + @@ -423,6 +433,11 @@ + + + + + diff --git a/src/main/resources/icons/externalLinter.svg b/src/main/resources/icons/externalLinter.svg new file mode 100644 index 000000000..4a4e0d54f --- /dev/null +++ b/src/main/resources/icons/externalLinter.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/icons/externalLinter_dark.svg b/src/main/resources/icons/externalLinter_dark.svg new file mode 100644 index 000000000..2cef3722a --- /dev/null +++ b/src/main/resources/icons/externalLinter_dark.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/inspectionDescriptions/MvExternalLinter.html b/src/main/resources/inspectionDescriptions/MvExternalLinter.html new file mode 100644 index 000000000..79b897e23 --- /dev/null +++ b/src/main/resources/inspectionDescriptions/MvExternalLinter.html @@ -0,0 +1,5 @@ + + +This inspection runs external linter to check for aptos-cli compiler errors. + + From c0f5f8165afee9864d0e74d26d897280e1a5d0e0 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Thu, 16 May 2024 22:23:46 +0300 Subject: [PATCH 07/14] fix project refresh on file creation --- .../org/move/cli/MoveProjectsService.kt | 16 ++- ...er.kt => OnMoveTomlCreatedFileListener.kt} | 10 +- .../MoveExternalSystemProjectAware.kt | 3 +- .../MoveExternalSystemProjectAwareTest.kt | 122 +++++++++++++++--- 4 files changed, 122 insertions(+), 29 deletions(-) rename src/main/kotlin/org/move/cli/{MoveTomlWatcher.kt => OnMoveTomlCreatedFileListener.kt} (63%) diff --git a/src/main/kotlin/org/move/cli/MoveProjectsService.kt b/src/main/kotlin/org/move/cli/MoveProjectsService.kt index 163528c0e..5ee620121 100644 --- a/src/main/kotlin/org/move/cli/MoveProjectsService.kt +++ b/src/main/kotlin/org/move/cli/MoveProjectsService.kt @@ -7,8 +7,10 @@ import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.components.service import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.logger +import com.intellij.openapi.externalSystem.autoimport.AutoImportProjectNotificationAware import com.intellij.openapi.externalSystem.autoimport.AutoImportProjectTracker import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTracker +import com.intellij.openapi.externalSystem.autoimport.changes.FilesChangesListener import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.project.Project @@ -20,6 +22,7 @@ import com.intellij.openapi.roots.ex.ProjectRootManagerEx import com.intellij.openapi.util.EmptyRunnable import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.ex.temp.TempFileSystem import com.intellij.psi.PsiDirectory import com.intellij.psi.PsiElement @@ -52,9 +55,6 @@ val Project.hasMoveProject get() = this.moveProjectsService.allProjects.isNotEmp class MoveProjectsService(val project: Project): Disposable { -// private val refreshOnBuildDirChangeWatcher = -// BuildDirectoryWatcher(emptyList()) { scheduleProjectsRefresh("build/ directory changed") } - var initialized = false init { @@ -100,6 +100,16 @@ class MoveProjectsService(val project: Project): Disposable { } } }) + + // default projectTracker cannot detect Move.toml file creation, + // as it's not present in the `settingsFiles` + @Suppress("UnstableApiUsage") + project.messageBus.connect(disposable) + .subscribe(VirtualFileManager.VFS_CHANGES, OnMoveTomlCreatedFileListener { + val tracker = AutoImportProjectTracker.getInstance(project) + tracker.markDirty(moveProjectAware.projectId) + tracker.scheduleProjectRefresh() + }) } // requires ReadAccess diff --git a/src/main/kotlin/org/move/cli/MoveTomlWatcher.kt b/src/main/kotlin/org/move/cli/OnMoveTomlCreatedFileListener.kt similarity index 63% rename from src/main/kotlin/org/move/cli/MoveTomlWatcher.kt rename to src/main/kotlin/org/move/cli/OnMoveTomlCreatedFileListener.kt index 5c5486665..7f0d3c28a 100644 --- a/src/main/kotlin/org/move/cli/MoveTomlWatcher.kt +++ b/src/main/kotlin/org/move/cli/OnMoveTomlCreatedFileListener.kt @@ -1,19 +1,21 @@ package org.move.cli import com.intellij.openapi.vfs.newvfs.BulkFileListener +import com.intellij.openapi.vfs.newvfs.events.VFileCreateEvent import com.intellij.openapi.vfs.newvfs.events.VFileEvent import com.intellij.openapi.vfs.newvfs.events.VFilePropertyChangeEvent -class MoveTomlWatcher( - private val onMoveTomlChange: () -> Unit +class OnMoveTomlCreatedFileListener( + private val onMoveTomlAdded: () -> Unit ) : BulkFileListener { override fun after(events: List) { - if (events.any { isInterestingEvent(it) }) onMoveTomlChange() + if (events.any { isInterestingEvent(it) }) onMoveTomlAdded() } private fun isInterestingEvent(event: VFileEvent): Boolean { - return event.pathEndsWith(Consts.MANIFEST_FILE) + return event is VFileCreateEvent && event.path.endsWith(Consts.MANIFEST_FILE) + || event is VFilePropertyChangeEvent && event.newPath.endsWith(Consts.MANIFEST_FILE) } } diff --git a/src/main/kotlin/org/move/cli/externalSystem/MoveExternalSystemProjectAware.kt b/src/main/kotlin/org/move/cli/externalSystem/MoveExternalSystemProjectAware.kt index 6b5031c7f..d1a2a147d 100644 --- a/src/main/kotlin/org/move/cli/externalSystem/MoveExternalSystemProjectAware.kt +++ b/src/main/kotlin/org/move/cli/externalSystem/MoveExternalSystemProjectAware.kt @@ -21,7 +21,8 @@ class MoveExternalSystemProjectAware( get() { val settingsFilesService = MoveSettingsFilesService.getInstance(project) // Always collect fresh settings files - return settingsFilesService.collectSettingsFiles() + val settingsFiles = settingsFilesService.collectSettingsFiles() + return settingsFiles } override fun reloadProject(context: ExternalSystemProjectReloadContext) { diff --git a/src/test/kotlin/org/move/cli/externalSystem/MoveExternalSystemProjectAwareTest.kt b/src/test/kotlin/org/move/cli/externalSystem/MoveExternalSystemProjectAwareTest.kt index 4623e913f..51e6de1b4 100644 --- a/src/test/kotlin/org/move/cli/externalSystem/MoveExternalSystemProjectAwareTest.kt +++ b/src/test/kotlin/org/move/cli/externalSystem/MoveExternalSystemProjectAwareTest.kt @@ -38,11 +38,13 @@ class MoveExternalSystemProjectAwareTest: MvProjectTestBase() { namedMoveToml("RootPackage") build { dir("RootPackage") { - buildInfoYaml(""" + buildInfoYaml( + """ --- compiled_package_info: package_name: RootPackage -""") +""" + ) } } sources { @@ -85,33 +87,98 @@ compiled_package_info: testProject.checkFileModification("child2/sources/main.move", triggered = false) } - fun `test reload`() { + fun `test files deletion`() { val testProject = testProject { - moveToml(""" + namedMoveToml("Liquidswap") + sources { + main( + """ + module 0x1::main {/*caret*/} + """ + ) + } + dir("liquidswap_init") { + namedMoveToml("LiquidswapInit") + sources { + move( + "liquidswap_init_main.move", """ + module 0x1::liquidswap_init_main {} + """ + ) + } + } + } + assertNotificationAware(event = "after project creation") + + testProject.checkFileDeletion("Move.toml", triggered = true) + testProject.checkFileDeletion("sources/main.move", triggered = false) + + testProject.checkFileDeletion("liquidswap_init/Move.toml", triggered = true) + testProject.checkFileDeletion("liquidswap_init/sources/liquidswap_init_main.move", triggered = false) + + val liquidswapInitDir = testProject.file("liquidswap_init") + checkModification( + "removing of ", "liquidswap_init", triggered = true, + apply = { liquidswapInitDir.delete(liquidswapInitDir.fileSystem) }, + revert = null + ) + } + +// fun `test files creation`() { +// val testProject = testProject { +// namedMoveToml("Liquidswap") +// sources { +// main("/*caret*/") +// } +// dir("liquidswap_init") { +// sources { } +// } +// } +// assertNotificationAware(event = "initial project creation") +// +// testProject.checkFileCreation("sources/module.move", revert = false, triggered = false) +// +// testProject.checkFileCreation("liquidswap_init/Move.toml", triggered = true) +// testProject.checkFileCreation("liquidswap_init/sources/main.move", triggered = false) +// +// // no trigger if malformed file name +// testProject.checkFileCreation("liquidswap_init/MyMove.toml", triggered = false, revert = false) +// testProject.checkFileRename("liquidswap_init/MyMove.toml", "Move.toml", triggered = true) +// } + + fun `test reloading`() { + val testProject = testProject { + moveToml( + """ [package] name = "MainPackage" [dependencies] #Dep = { local = "./dep" } - """) + """ + ) sources { - main(""" + main( + """ module 0x1::main { fun main() { 0x1::dep::call(); //^ } } - """) + """ + ) } dir("dep") { namedMoveToml("Dep") sources { - move("dep.move", """ + move( + "dep.move", """ module 0x1::dep { public fun call() {} } - """) + """ + ) } } } @@ -153,22 +220,32 @@ compiled_package_info: ) } - private fun TestProject.checkFileCreation(path: String, triggered: Boolean) { - checkModification("creation of", path, triggered, - apply = { createFile(rootDirectory, path) }, - revert = { - val file = file(path) - file.delete(file.fileSystem) - } + private fun TestProject.checkFileCreation(path: String, triggered: Boolean, revert: Boolean = true) { + val callRevert = if (revert) ({ val file = file(path); file.delete(file.fileSystem) }) else null + checkModification( + "creation of", path, triggered, + apply = { createFile(rootDirectory, path) }, + revert = callRevert ) } + @Throws(IOException::class) + private fun TestProject.checkFileRename(path: String, newName: String, triggered: Boolean): VirtualFile { + val file = file(path) + checkModification( + "rename of", path, triggered = triggered, + apply = { file.rename(null, newName) }, + revert = null + ) + return file + } + private fun checkModification( eventName: String, path: String, triggered: Boolean, apply: () -> Unit, - revert: () -> Unit + revert: (() -> Unit)? ) { runWriteAction { apply() @@ -176,10 +253,12 @@ compiled_package_info: val externalSystems = if (triggered) arrayOf(moveSystemId) else arrayOf() assertNotificationAware(*externalSystems, event = "$eventName $path") - runWriteAction { - revert() + if (revert != null) { + runWriteAction { + revert() + } + assertNotificationAware(event = "revert $eventName $path") } - assertNotificationAware(event = "revert $eventName $path") } private fun scheduleProjectReload() { @@ -221,7 +300,8 @@ compiled_package_info: val parentPath = PathUtil.getParentPath(path) var parent = root if (parentPath.isNotEmpty()) { - parent = VfsUtil.createDirectoryIfMissing(root, parentPath) ?: error("Failed to create $parentPath directory") + parent = VfsUtil.createDirectoryIfMissing(root, parentPath) + ?: error("Failed to create $parentPath directory") } val file = parent.createChildData(parent.fileSystem, name) VfsUtil.saveText(file, text) From e844189eb53876b0797dd727db38e25cb4dd677c Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Mon, 20 May 2024 03:13:42 +0300 Subject: [PATCH 08/14] recognize mv files, decompiler action --- .../move/bytecode/AptosBytecodeFileType.kt | 13 +++ .../AptosBytecodeNotificationProvider.kt | 71 ++++++++++++ .../org/move/bytecode/AptosDecompiler.kt | 103 ++++++++++++++++++ .../cli/runConfigurations/BlockchainCli.kt | 7 +- .../move/cli/runConfigurations/aptos/Aptos.kt | 17 ++- .../cli/settings/MvProjectSettingsService.kt | 4 +- src/main/kotlin/org/move/ide/MoveIcons.kt | 2 + ...MoveProjectDetectedNotificationProvider.kt | 7 ++ .../org/move/openapiext/CommandLineExt.kt | 37 +++++-- .../resources/META-INF/intellij-move-core.xml | 9 ++ 10 files changed, 255 insertions(+), 15 deletions(-) create mode 100644 src/main/kotlin/org/move/bytecode/AptosBytecodeFileType.kt create mode 100644 src/main/kotlin/org/move/bytecode/AptosBytecodeNotificationProvider.kt create mode 100644 src/main/kotlin/org/move/bytecode/AptosDecompiler.kt diff --git a/src/main/kotlin/org/move/bytecode/AptosBytecodeFileType.kt b/src/main/kotlin/org/move/bytecode/AptosBytecodeFileType.kt new file mode 100644 index 000000000..cfb49b048 --- /dev/null +++ b/src/main/kotlin/org/move/bytecode/AptosBytecodeFileType.kt @@ -0,0 +1,13 @@ +package org.move.bytecode + +import com.intellij.openapi.fileTypes.FileType +import org.move.ide.MoveIcons + +object AptosBytecodeFileType: FileType { + override fun getIcon() = MoveIcons.MV_LOGO + override fun getName() = "APTOS_BYTECODE" + override fun getDefaultExtension() = "mv" + override fun getDescription() = "Aptos Move bytecode" + override fun getDisplayName() = "Aptos Move bytecode" + override fun isBinary(): Boolean = true +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/bytecode/AptosBytecodeNotificationProvider.kt b/src/main/kotlin/org/move/bytecode/AptosBytecodeNotificationProvider.kt new file mode 100644 index 000000000..ed7c354d5 --- /dev/null +++ b/src/main/kotlin/org/move/bytecode/AptosBytecodeNotificationProvider.kt @@ -0,0 +1,71 @@ +package org.move.bytecode + +import com.intellij.ide.util.PropertiesComponent +import com.intellij.notification.NotificationType.ERROR +import com.intellij.openapi.fileEditor.FileEditor +import com.intellij.openapi.fileTypes.FileTypeRegistry +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.progress.Task +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.ui.EditorNotificationPanel +import com.intellij.ui.EditorNotificationProvider +import org.move.ide.notifications.showBalloon +import org.move.ide.notifications.updateAllNotifications +import org.move.openapiext.openFile +import org.move.openapiext.toVirtualFile +import org.move.stdext.unwrapOrElse +import java.util.function.Function +import javax.swing.JComponent +import kotlin.io.path.exists + +class AptosBytecodeNotificationProvider: EditorNotificationProvider { + override fun collectNotificationData( + project: Project, + file: VirtualFile + ): Function? { + if (!FileTypeRegistry.getInstance().isFileOfType(file, AptosBytecodeFileType)) { + return null + } + + val aptosDecompiler = AptosBytecodeDecompiler() + val targetFileDir = aptosDecompiler.getDecompilerTargetFileDirOnTemp(project, file)!! + + val expectedTargetFile = targetFileDir.resolve(aptosDecompiler.hashedSourceFileName(file)) + val properties = PropertiesComponent.getInstance(project) + + val triedKey = DECOMPILATION_TRIED + "-" + file.path + "-" + aptosDecompiler.hashedSourceFileName(file) + if (!expectedTargetFile.exists()) { + if (properties.getBoolean(triedKey, false)) { + return Function { + EditorNotificationPanel(it).apply { + text = "Error with decompilation occurred" + } + } + } + object : Task.Backgroundable(project, "Decompiling ${file.name}...", true) { + override fun run(indicator: ProgressIndicator) { + aptosDecompiler.decompileFile(project, file, targetFileDir) + .unwrapOrElse { + project.showBalloon("Error with decompilation process", it, ERROR) + } + properties.setValue(triedKey, true) + updateAllNotifications(project) + } + }.queue() + return null + } else { + return Function { + EditorNotificationPanel(it).apply { + createActionLabel("Show decompiled source code") { + project.openFile(expectedTargetFile.toVirtualFile()!!) + } + } + } + } + } + + companion object { + private const val DECOMPILATION_TRIED = "org.move.aptosDecompilerNotificationKey" + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/bytecode/AptosDecompiler.kt b/src/main/kotlin/org/move/bytecode/AptosDecompiler.kt new file mode 100644 index 000000000..f1bdd4c3e --- /dev/null +++ b/src/main/kotlin/org/move/bytecode/AptosDecompiler.kt @@ -0,0 +1,103 @@ +package org.move.bytecode + +import com.intellij.openapi.Disposable +import com.intellij.openapi.fileTypes.BinaryFileDecompiler +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.openapi.vfs.newvfs.BulkFileListener +import com.intellij.openapi.vfs.newvfs.events.VFileEvent +import com.intellij.openapi.vfs.readText +import com.intellij.openapi.vfs.toNioPathOrNull +import org.move.cli.settings.getAptosCli +import org.move.openapiext.pathAsPath +import org.move.openapiext.rootDisposable +import org.move.openapiext.rootPath +import org.move.stdext.RsResult +import org.move.stdext.unwrapOrElse +import java.io.File +import java.nio.file.Path +import kotlin.io.path.exists +import kotlin.io.path.relativeTo + +// todo: this is disabled for now, it's a process under ReadAction, and needs to be run in the indexing phase +class AptosBytecodeDecompiler: BinaryFileDecompiler { + override fun decompile(file: VirtualFile): CharSequence { + return file.readText() +// val project = +// ProjectLocator.getInstance().getProjectsForFile(file).firstOrNull { it?.isAptosConfigured == true } +// ?: ProjectManager.getInstance().defaultProject.takeIf { it.isAptosConfigured } +// ?: return file.readText() +// val targetFileDir = getDecompilerTargetFileDirOnTemp(project, file) ?: return file.readText() +// val targetFile = decompileFile(project, file, targetFileDir) ?: return file.readText() +// return LoadTextUtil.loadText(targetFile) + } + + fun decompileFile(project: Project, file: VirtualFile, targetFileDir: Path): RsResult { + val disposable = project.createDisposableOnFileChange(file) + val aptos = project.getAptosCli(disposable) ?: return RsResult.Err("No Aptos CLI configured") + + if (!targetFileDir.exists()) { + targetFileDir.toFile().mkdirs() + } + + aptos.decompileFile(file.path, outputDir = targetFileDir.toString()) + .unwrapOrElse { + return RsResult.Err("`aptos move decompile` failed") + } + val decompiledName = sourceFileName(file) + val decompiledFile = + VirtualFileManager.getInstance().findFileByNioPath(targetFileDir.resolve(decompiledName)) ?: run { + // something went wrong, no output file + return RsResult.Err("Cannot find decompiled file in the target directory") + } + + val decompiledNioFile = decompiledFile.toNioPathOrNull()?.toFile() + ?: return RsResult.Err("Cannot convert VirtualFile to File") + FileUtil.rename(decompiledNioFile, hashedSourceFileName(file)) + + return RsResult.Ok(decompiledFile) + } + + fun getDecompilerTargetFileDirOnTemp(project: Project, file: VirtualFile): Path? { + val rootDecompilerDir = getArtifactsDir() + val projectDecompilerDir = rootDecompilerDir.resolve(project.name) + val root = project.rootPath ?: return null + val relativeFilePath = file.parent.pathAsPath.relativeTo(root) + val targetFileDir = projectDecompilerDir.toPath().resolve(relativeFilePath) + return targetFileDir + } + + fun getArtifactsDir(): File { + return File(FileUtil.getTempDirectory(), "intellij-move-decompiled-artifacts") + } + + fun sourceFileName(file: VirtualFile): String { + val fileName = file.name + return "$fileName.move" + } + + fun hashedSourceFileName(file: VirtualFile): String { + val fileName = file.name + return "$fileName#decompiled.move" + } +} + +fun Project.createDisposableOnFileChange(file: VirtualFile): Disposable { + val filePath = file.path + val disposable = Disposer.newDisposable("Dispose on any change to the ${file.name} file") + messageBus.connect(disposable).subscribe( + VirtualFileManager.VFS_CHANGES, + object: BulkFileListener { + override fun after(events: MutableList) { + if (events.any { it.path == filePath }) { + Disposer.dispose(disposable) + } + } + } + ) + Disposer.register(this.rootDisposable, disposable) + return disposable +} diff --git a/src/main/kotlin/org/move/cli/runConfigurations/BlockchainCli.kt b/src/main/kotlin/org/move/cli/runConfigurations/BlockchainCli.kt index d888df2ad..7860c2f5d 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/BlockchainCli.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/BlockchainCli.kt @@ -1,5 +1,6 @@ package org.move.cli.runConfigurations +import com.intellij.execution.process.CapturingProcessHandler import com.intellij.execution.process.ProcessListener import com.intellij.execution.process.ProcessOutput import com.intellij.openapi.Disposable @@ -8,6 +9,7 @@ import com.intellij.openapi.util.Disposer import com.intellij.openapi.vfs.VirtualFile import org.move.openapiext.RsProcessResult import org.move.openapiext.execute +import org.move.openapiext.runProcessWithGlobalProgress import java.nio.file.Path abstract class BlockchainCli(parentDisposable: Disposable?): Disposable { @@ -37,10 +39,11 @@ abstract class BlockchainCli(parentDisposable: Disposable?): Disposable { protected fun executeCommandLine( commandLine: CliCommandLineArgs, - listener: ProcessListener? = null + listener: ProcessListener? = null, + runner: CapturingProcessHandler.() -> ProcessOutput = { runProcessWithGlobalProgress(timeoutInMilliseconds = null) } ): RsProcessResult { return commandLine .toGeneralCommandLine(this.cliLocation) - .execute(this, stdIn = null, listener = listener) + .execute(this, stdIn = null, listener = listener, runner = runner) } } \ No newline at end of file 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 4f4712a97..2bc12edf6 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt @@ -4,6 +4,8 @@ import com.intellij.execution.configuration.EnvironmentVariablesData import com.intellij.execution.process.ProcessListener import com.intellij.execution.process.ProcessOutput import com.intellij.openapi.Disposable +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import com.intellij.util.execution.ParametersListUtil @@ -19,6 +21,7 @@ import org.move.openapiext.common.isUnitTestMode import org.move.stdext.RsResult import org.move.stdext.RsResult.Err import org.move.stdext.RsResult.Ok +import org.move.stdext.buildList import org.move.stdext.unwrapOrElse import java.nio.file.Path import java.nio.file.Paths @@ -132,13 +135,23 @@ data class Aptos( return executeCommandLine(commandLine) } - fun decompileFile(bytecodeFilePath: String): RsProcessResult { + fun decompileFile( + bytecodeFilePath: String, + outputDir: String?, + ): RsProcessResult { val fileRoot = Paths.get(bytecodeFilePath).parent val commandLine = CliCommandLineArgs( subCommand = "move decompile", - arguments = listOf("--bytecode-path", bytecodeFilePath), + arguments = buildList { + add("--bytecode-path"); add(bytecodeFilePath) + if (outputDir != null) { + add("--output-dir"); add(outputDir) + } + add("--assume-yes") + }, workingDirectory = fileRoot ) + // only one second is allowed to run decompiler, otherwise fails with timeout return executeCommandLine(commandLine) } } diff --git a/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt b/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt index c9c6504a3..a75e77db0 100644 --- a/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt +++ b/src/main/kotlin/org/move/cli/settings/MvProjectSettingsService.kt @@ -137,8 +137,8 @@ fun Project.getBlockchainCli(blockchain: Blockchain, parentDisposable: Disposabl fun Project.getBlockchainCli(parentDisposable: Disposable?): BlockchainCli? = getBlockchainCli(this.blockchain, parentDisposable) -fun Project.getAptosCli(parentDisposable: Disposable? = null): Aptos? = - getBlockchainCli(APTOS, parentDisposable) as? Aptos +val Project.isAptosConfigured: Boolean get() = getBlockchainCli(APTOS) != null +fun Project.getAptosCli(parentDisposable: Disposable? = null): Aptos? = getBlockchainCli(APTOS, parentDisposable) as? Aptos fun Project.getSuiCli(parentDisposable: Disposable? = null): Sui? = getBlockchainCli(SUI, parentDisposable) as? Sui diff --git a/src/main/kotlin/org/move/ide/MoveIcons.kt b/src/main/kotlin/org/move/ide/MoveIcons.kt index 1210eed17..2bf229f9a 100644 --- a/src/main/kotlin/org/move/ide/MoveIcons.kt +++ b/src/main/kotlin/org/move/ide/MoveIcons.kt @@ -18,6 +18,8 @@ object MoveIcons { val APTOS_LOGO = load("/icons/aptos.svg") val SUI_LOGO = load("/icons/sui.svg") + val MV_LOGO = load("/icons/move_logo.svg") + val ADDRESS = load("/icons/annotationtype.png") val MODULE = load("/icons/module.svg") diff --git a/src/main/kotlin/org/move/ide/notifications/NoMoveProjectDetectedNotificationProvider.kt b/src/main/kotlin/org/move/ide/notifications/NoMoveProjectDetectedNotificationProvider.kt index 3dd9eee84..13fcec105 100644 --- a/src/main/kotlin/org/move/ide/notifications/NoMoveProjectDetectedNotificationProvider.kt +++ b/src/main/kotlin/org/move/ide/notifications/NoMoveProjectDetectedNotificationProvider.kt @@ -6,6 +6,7 @@ import com.intellij.openapi.project.DumbAware import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import com.intellij.ui.EditorNotificationPanel +import org.move.bytecode.AptosBytecodeDecompiler import org.move.cli.MoveProjectsService import org.move.cli.MoveProjectsService.Companion.MOVE_PROJECTS_TOPIC import org.move.cli.moveProjectsService @@ -14,6 +15,7 @@ import org.move.cli.settings.MvProjectSettingsServiceBase.Companion.MOVE_SETTING import org.move.cli.settings.moveSettings import org.move.lang.isMoveFile import org.move.lang.isMoveTomlManifestFile +import org.move.lang.toNioPathOrNull import org.move.openapiext.common.isDispatchThread import org.move.openapiext.common.isUnitTestMode @@ -43,6 +45,11 @@ class NoMoveProjectDetectedNotificationProvider(project: Project): MvEditorNotif @Suppress("UnstableApiUsage") if (!project.isTrusted()) return null + // check whether file is a decompiler artifact + val decompiledArtifactsDir = AptosBytecodeDecompiler().getArtifactsDir() + val asNioFile = file.toNioPathOrNull()?.toFile() + if (asNioFile == null || asNioFile.relativeToOrNull(decompiledArtifactsDir) != null) return null + val blockchain = project.moveSettings.blockchain val moveProjectsService = project.moveProjectsService // HACK: Reloads projects once on an opening of any Move file, if not yet reloaded. diff --git a/src/main/kotlin/org/move/openapiext/CommandLineExt.kt b/src/main/kotlin/org/move/openapiext/CommandLineExt.kt index a748d4c86..17fdb40d6 100644 --- a/src/main/kotlin/org/move/openapiext/CommandLineExt.kt +++ b/src/main/kotlin/org/move/openapiext/CommandLineExt.kt @@ -144,15 +144,34 @@ fun GeneralCommandLine.execute( private fun showCommandLineBalloon(commandLineString: String, output: ProcessOutput) { if (isDebugModeEnabled()) { - val content = - if (!output.isSuccess) { - """`$commandLineString` |Execution failed (exit code ${output.exitCode}). - """ - } else { - """`$commandLineString` |Execution successful. - """ + when { + output.isTimeout -> { + showBalloonWithoutProject( + "Execution failed", + "`$commandLineString`(timeout)", + INFORMATION + ) + } + output.isCancelled -> { + showBalloonWithoutProject( + "Execution failed", + "`$commandLineString`(cancelled)", + INFORMATION + ) + } + output.exitCode != 0 -> { + showBalloonWithoutProject( + "Execution failed", + "`$commandLineString`(exit code ${output.exitCode}). " + + "

stdout=${output.stdout}

" + + "

stderr=${output.stderr}

", + INFORMATION + ) } - showBalloonWithoutProject(content.trimStart(), INFORMATION) + else -> { + showBalloonWithoutProject("Execution successful", commandLineString, INFORMATION) + } + } } } @@ -163,7 +182,7 @@ private fun errorMessage(commandLine: GeneralCommandLine, output: ProcessOutput) |stderr : ${output.stderr} """.trimMargin() -private fun CapturingProcessHandler.runProcessWithGlobalProgress(timeoutInMilliseconds: Int? = null): ProcessOutput { +fun CapturingProcessHandler.runProcessWithGlobalProgress(timeoutInMilliseconds: Int? = null): ProcessOutput { return runProcess(ProgressManager.getGlobalProgressIndicator(), timeoutInMilliseconds) } diff --git a/src/main/resources/META-INF/intellij-move-core.xml b/src/main/resources/META-INF/intellij-move-core.xml index 8e0e6ab3a..249d71f36 100644 --- a/src/main/resources/META-INF/intellij-move-core.xml +++ b/src/main/resources/META-INF/intellij-move-core.xml @@ -18,6 +18,15 @@ org.toml.lang + + + + + Date: Wed, 22 May 2024 00:32:34 +0300 Subject: [PATCH 09/14] decompiler action with context menu and notifications --- .../AptosBytecodeNotificationProvider.kt | 107 ++++++++++++----- .../org/move/bytecode/AptosDecompiler.kt | 33 ++++- .../bytecode/DecompileAptosMvFileAction.kt | 39 ++++++ .../move/bytecode/FetchAptosPackageAction.kt | 14 +++ .../move/bytecode/FetchAptosPackageDialog.kt | 113 ++++++++++++++++++ .../move/cli/runConfigurations/aptos/Aptos.kt | 27 +++-- .../aptos/RunAptosCommandActionBase.kt | 10 +- .../org/move/ide/notifications/Utils.kt | 21 ++++ .../resources/META-INF/intellij-move-core.xml | 11 ++ 9 files changed, 329 insertions(+), 46 deletions(-) create mode 100644 src/main/kotlin/org/move/bytecode/DecompileAptosMvFileAction.kt create mode 100644 src/main/kotlin/org/move/bytecode/FetchAptosPackageAction.kt create mode 100644 src/main/kotlin/org/move/bytecode/FetchAptosPackageDialog.kt diff --git a/src/main/kotlin/org/move/bytecode/AptosBytecodeNotificationProvider.kt b/src/main/kotlin/org/move/bytecode/AptosBytecodeNotificationProvider.kt index ed7c354d5..1ac342362 100644 --- a/src/main/kotlin/org/move/bytecode/AptosBytecodeNotificationProvider.kt +++ b/src/main/kotlin/org/move/bytecode/AptosBytecodeNotificationProvider.kt @@ -5,21 +5,34 @@ import com.intellij.notification.NotificationType.ERROR import com.intellij.openapi.fileEditor.FileEditor import com.intellij.openapi.fileTypes.FileTypeRegistry 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.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.openapi.vfs.newvfs.BulkFileListener +import com.intellij.openapi.vfs.newvfs.events.VFileEvent import com.intellij.ui.EditorNotificationPanel import com.intellij.ui.EditorNotificationProvider -import org.move.ide.notifications.showBalloon +import org.move.ide.notifications.showDebugBalloon import org.move.ide.notifications.updateAllNotifications import org.move.openapiext.openFile -import org.move.openapiext.toVirtualFile +import org.move.openapiext.pathAsPath +import org.move.stdext.RsResult import org.move.stdext.unwrapOrElse import java.util.function.Function import javax.swing.JComponent -import kotlin.io.path.exists -class AptosBytecodeNotificationProvider: EditorNotificationProvider { +class AptosBytecodeNotificationProvider(project: Project): EditorNotificationProvider { + + init { + project.messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, object: BulkFileListener { + override fun after(events: MutableList) { + updateAllNotifications(project) + } + }) + } + override fun collectNotificationData( project: Project, file: VirtualFile @@ -27,45 +40,77 @@ class AptosBytecodeNotificationProvider: EditorNotificationProvider { if (!FileTypeRegistry.getInstance().isFileOfType(file, AptosBytecodeFileType)) { return null } + val properties = PropertiesComponent.getInstance(project) + val progressManager = ProgressManager.getInstance() + val decompilationFailedKey = DECOMPILATION_FAILED + "." + file.path val aptosDecompiler = AptosBytecodeDecompiler() - val targetFileDir = aptosDecompiler.getDecompilerTargetFileDirOnTemp(project, file)!! + val decompiledFilePath = file.parent.pathAsPath.resolve(aptosDecompiler.sourceFileName(file)) + val decompilationTask = DecompilationModalTask(project, file) - val expectedTargetFile = targetFileDir.resolve(aptosDecompiler.hashedSourceFileName(file)) - val properties = PropertiesComponent.getInstance(project) - - val triedKey = DECOMPILATION_TRIED + "-" + file.path + "-" + aptosDecompiler.hashedSourceFileName(file) - if (!expectedTargetFile.exists()) { - if (properties.getBoolean(triedKey, false)) { - return Function { - EditorNotificationPanel(it).apply { - text = "Error with decompilation occurred" + return Function { + EditorNotificationPanel(it).apply { + val existingDecompiledFile = + VirtualFileManager.getInstance().refreshAndFindFileByNioPath(decompiledFilePath) + if (existingDecompiledFile != null) { + // file exists + text = "Decompiled source file exists" + createActionLabel("Open source file") { + project.openFile(existingDecompiledFile) } + return@apply } - } - object : Task.Backgroundable(project, "Decompiling ${file.name}...", true) { - override fun run(indicator: ProgressIndicator) { - aptosDecompiler.decompileFile(project, file, targetFileDir) - .unwrapOrElse { - project.showBalloon("Error with decompilation process", it, ERROR) + + // decompiledFile does not exist + val decompilationFailed = properties.getBoolean(decompilationFailedKey, false) + if (decompilationFailed) { + text = "Decompilation command failed" + createActionLabel("Try again") { + val virtualFile = progressManager.run(decompilationTask) + .unwrapOrElse { + // something went wrong with the decompilation command again + project.showDebugBalloon("Error with decompilation process", it, ERROR) + return@createActionLabel + } + + properties.setValue(decompilationFailedKey, false) + project.openFile(virtualFile) + updateAllNotifications(project) + } + } else { + createActionLabel("Decompile into source code") { + val decompiledFile = progressManager.run(decompilationTask) + .unwrapOrElse { + project.showDebugBalloon("Error with decompilation process", it, ERROR) + return@unwrapOrElse null + } + if (decompiledFile == null) { + // something went wrong with the decompilation command + properties.setValue(decompilationFailedKey, true) + } else { + project.openFile(decompiledFile) } - properties.setValue(triedKey, true) - updateAllNotifications(project) - } - }.queue() - return null - } else { - return Function { - EditorNotificationPanel(it).apply { - createActionLabel("Show decompiled source code") { - project.openFile(expectedTargetFile.toVirtualFile()!!) + updateAllNotifications(project) } } } } } + class DecompilationModalTask(project: Project, val file: VirtualFile): + Task.WithResult, Exception>( + project, + "Decompiling ${file.name}...", + true + ) { + override fun compute(indicator: ProgressIndicator): RsResult { + val aptosDecompiler = AptosBytecodeDecompiler() + return aptosDecompiler.decompileFileToTheSameDir(project, file) + } + } + companion object { - private const val DECOMPILATION_TRIED = "org.move.aptosDecompilerNotificationKey" + private const val DECOMPILATION_FAILED = "org.move.aptosDecompilerNotificationKey" + } } \ No newline at end of file diff --git a/src/main/kotlin/org/move/bytecode/AptosDecompiler.kt b/src/main/kotlin/org/move/bytecode/AptosDecompiler.kt index f1bdd4c3e..fbbeacd90 100644 --- a/src/main/kotlin/org/move/bytecode/AptosDecompiler.kt +++ b/src/main/kotlin/org/move/bytecode/AptosDecompiler.kt @@ -1,16 +1,15 @@ package org.move.bytecode import com.intellij.openapi.Disposable +import com.intellij.openapi.fileEditor.impl.LoadTextUtil import com.intellij.openapi.fileTypes.BinaryFileDecompiler import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.io.FileUtil -import com.intellij.openapi.vfs.VirtualFile -import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.openapi.util.text.StringUtil +import com.intellij.openapi.vfs.* import com.intellij.openapi.vfs.newvfs.BulkFileListener import com.intellij.openapi.vfs.newvfs.events.VFileEvent -import com.intellij.openapi.vfs.readText -import com.intellij.openapi.vfs.toNioPathOrNull import org.move.cli.settings.getAptosCli import org.move.openapiext.pathAsPath import org.move.openapiext.rootDisposable @@ -25,7 +24,14 @@ import kotlin.io.path.relativeTo // todo: this is disabled for now, it's a process under ReadAction, and needs to be run in the indexing phase class AptosBytecodeDecompiler: BinaryFileDecompiler { override fun decompile(file: VirtualFile): CharSequence { - return file.readText() + val fileText = file.readText() + try { + StringUtil.assertValidSeparators(fileText) + return fileText + } catch (e: AssertionError) { + val bytes = file.readBytes() + return LoadTextUtil.getTextByBinaryPresentation(bytes, file) + } // val project = // ProjectLocator.getInstance().getProjectsForFile(file).firstOrNull { it?.isAptosConfigured == true } // ?: ProjectManager.getInstance().defaultProject.takeIf { it.isAptosConfigured } @@ -35,6 +41,23 @@ class AptosBytecodeDecompiler: BinaryFileDecompiler { // return LoadTextUtil.loadText(targetFile) } + fun decompileFileToTheSameDir(project: Project, file: VirtualFile): RsResult { + val disposable = project.createDisposableOnFileChange(file) + val aptos = project.getAptosCli(disposable) ?: return RsResult.Err("No Aptos CLI configured") + + aptos.decompileFile(file.path, outputDir = null) + .unwrapOrElse { + return RsResult.Err("`aptos move decompile` failed") + } + val decompiledFilePath = file.parent.pathAsPath.resolve(sourceFileName(file)) + val decompiledFile = VirtualFileManager.getInstance().refreshAndFindFileByNioPath(decompiledFilePath) + ?: run { + // something went wrong, no output file + return RsResult.Err("Expected decompiled file $decompiledFilePath does not exist") + } + return RsResult.Ok(decompiledFile) + } + fun decompileFile(project: Project, file: VirtualFile, targetFileDir: Path): RsResult { val disposable = project.createDisposableOnFileChange(file) val aptos = project.getAptosCli(disposable) ?: return RsResult.Err("No Aptos CLI configured") diff --git a/src/main/kotlin/org/move/bytecode/DecompileAptosMvFileAction.kt b/src/main/kotlin/org/move/bytecode/DecompileAptosMvFileAction.kt new file mode 100644 index 000000000..18dab12db --- /dev/null +++ b/src/main/kotlin/org/move/bytecode/DecompileAptosMvFileAction.kt @@ -0,0 +1,39 @@ +package org.move.bytecode + +import com.intellij.notification.NotificationType.ERROR +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.progress.ProgressManager +import com.intellij.openapi.project.DumbAwareAction +import org.move.bytecode.AptosBytecodeNotificationProvider.DecompilationModalTask +import org.move.cli.settings.getAptosCli +import org.move.ide.MoveIcons +import org.move.ide.notifications.showBalloon +import org.move.openapiext.openFile +import org.move.stdext.unwrapOrElse +import java.util.* + +class DecompileAptosMvFileAction: DumbAwareAction("Decompile .mv File", null, MoveIcons.APTOS_LOGO) { + override fun actionPerformed(e: AnActionEvent) { + val project = e.project ?: return + val file = e.getData(CommonDataKeys.PSI_FILE)?.virtualFile ?: return + val decompilationTask = DecompilationModalTask(project, file) + val decompiledFile = ProgressManager.getInstance().run(decompilationTask) + .unwrapOrElse { + project.showBalloon("Error with decompilation process", it, ERROR) + return + } + project.openFile(decompiledFile) + } + + override fun update(e: AnActionEvent) { + val file = e.getData(CommonDataKeys.PSI_FILE) + val presentation = e.presentation + val enabled = + (file != null + && Objects.nonNull(file.virtualFile) && !(file.virtualFile.fileSystem.isReadOnly) + && file.fileType == AptosBytecodeFileType + && e.getData(CommonDataKeys.PROJECT)?.getAptosCli() != null) + presentation.isEnabledAndVisible = enabled + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/bytecode/FetchAptosPackageAction.kt b/src/main/kotlin/org/move/bytecode/FetchAptosPackageAction.kt new file mode 100644 index 000000000..89dd06d25 --- /dev/null +++ b/src/main/kotlin/org/move/bytecode/FetchAptosPackageAction.kt @@ -0,0 +1,14 @@ +package org.move.bytecode + +import com.intellij.openapi.actionSystem.AnActionEvent +import org.move.cli.runConfigurations.aptos.RunAptosCommandActionBase + +class FetchAptosPackageAction: RunAptosCommandActionBase("Fetch on-chain package") { + override fun actionPerformed(e: AnActionEvent) { + val project = e.project ?: return + + val parametersDialog = FetchAptosPackageDialog(project) + parametersDialog.show() + } + +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/bytecode/FetchAptosPackageDialog.kt b/src/main/kotlin/org/move/bytecode/FetchAptosPackageDialog.kt new file mode 100644 index 000000000..5269e63dc --- /dev/null +++ b/src/main/kotlin/org/move/bytecode/FetchAptosPackageDialog.kt @@ -0,0 +1,113 @@ +package org.move.bytecode + +import com.intellij.execution.process.ProcessOutput +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory +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.ui.DialogWrapper +import com.intellij.openapi.ui.ValidationInfo +import com.intellij.ui.components.JBCheckBox +import com.intellij.ui.components.JBTextField +import com.intellij.ui.dsl.builder.AlignX +import com.intellij.ui.dsl.builder.panel +import org.move.cli.settings.getAptosCli +import org.move.openapiext.RsProcessResult +import org.move.openapiext.pathField +import org.move.stdext.unwrapOrElse +import javax.swing.JComponent +import kotlin.io.path.Path + +class FetchAptosPackageDialog(val project: Project): DialogWrapper(project, true) { + + val addressTextField = JBTextField() + val packageTextField = JBTextField() + val outputDirField = pathField( + FileChooserDescriptorFactory.createSingleFolderDescriptor(), + this.disposable, + "Output Directory" + ) + val decompileCheckbox = JBCheckBox("Decompile afterwards") + + val profileField = JBTextField("default") + val nodeApiKey = JBTextField() + val connectionTimeout = JBTextField() + + init { + title = "Aptos Decompiler" + setSize(600, 400) + + outputDirField.text = project.basePath.orEmpty() + decompileCheckbox.isSelected = true + init() + } + + override fun createCenterPanel(): JComponent { + return panel { + row("Address:") { cell(addressTextField).align(AlignX.FILL) } + row("Package:") { cell(packageTextField).align(AlignX.FILL) } + row("Output directory:") { cell(outputDirField).align(AlignX.FILL) } + row { cell(decompileCheckbox) } + + val parametersGroup = collapsibleGroup("Connection Parameters") { + row("Profile:") { cell(profileField) } + row("Node API Key:") { cell(nodeApiKey) } + row("Connection timeout:") { cell(connectionTimeout) } + } + parametersGroup.expanded = false + + } + } + + override fun doOKAction() { + val accountAddress = this.addressTextField.text + val packageName = this.packageTextField.text +// val profile = this.profileField.text + val outputDir = this.outputDirField.text + val decompile = this.decompileCheckbox.isSelected + val aptos = project.getAptosCli(this.disposable) ?: return + + val downloadTask = object: Task.WithResult, Exception>( + project, + "Downloading $accountAddress::$packageName...", + true + ) { + override fun compute(indicator: ProgressIndicator): RsProcessResult { + return aptos.downloadPackage(project, accountAddress, packageName, outputDir, + runner = { runProcessWithProgressIndicator(indicator) }) + } + } + ProgressManager.getInstance().run(downloadTask) + .unwrapOrElse { + this.setErrorText(it.message) + return + } + + if (decompile) { + val decompileTask = object: Task.WithResult, Exception>( + project, + "Decompiling $accountAddress::$packageName...", + true + ) { + override fun compute(indicator: ProgressIndicator): RsProcessResult { + val downloadedPath = Path(outputDir).resolve(packageName) + return aptos.decompileDownloadedPackage(downloadedPath) + } + } + ProgressManager.getInstance().run(decompileTask) + .unwrapOrElse { + this.setErrorText(it.message) + return + } + } + + super.doOKAction() + } + + override fun getPreferredFocusedComponent(): JComponent = addressTextField + + override fun doValidate(): ValidationInfo? { + return super.doValidate() + } +} \ No newline at end of file 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 2bc12edf6..3fe6d1d69 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt @@ -1,11 +1,10 @@ package org.move.cli.runConfigurations.aptos import com.intellij.execution.configuration.EnvironmentVariablesData +import com.intellij.execution.process.CapturingProcessHandler import com.intellij.execution.process.ProcessListener import com.intellij.execution.process.ProcessOutput import com.intellij.openapi.Disposable -import com.intellij.openapi.progress.ProgressIndicator -import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.VirtualFile import com.intellij.util.execution.ParametersListUtil @@ -111,17 +110,24 @@ data class Aptos( } fun downloadPackage( - moveProject: MoveProject, + project: Project, accountAddress: String, - packageName: String - ): RsResult { + packageName: String, + outputDir: String, + runner: CapturingProcessHandler.() -> ProcessOutput = { runProcessWithGlobalProgress(timeoutInMilliseconds = null) } + ): RsProcessResult { val commandLine = CliCommandLineArgs( subCommand = "move download", - arguments = listOf("--account", accountAddress, "--package", packageName, "--bytecode"), - workingDirectory = moveProject.workingDirectory, + arguments = buildList { + add("--account"); add(accountAddress) + add("--package"); add(packageName) + add("--bytecode") + add("--output-dir"); add(outputDir) + }, + workingDirectory = project.basePath?.let { Path.of(it) }, environmentVariables = EnvironmentVariablesData.DEFAULT ) - return executeCommandLine(commandLine).ignoreExitCode() + return executeCommandLine(commandLine, runner = runner) } fun decompileDownloadedPackage(downloadedPackagePath: Path): RsProcessResult { @@ -129,7 +135,10 @@ data class Aptos( downloadedPackagePath.resolve("bytecode_modules").toAbsolutePath().toString() val commandLine = CliCommandLineArgs( subCommand = "move decompile", - arguments = listOf("--package-path", bytecodeModulesPath), + arguments = buildList { + add("--package-path"); add(bytecodeModulesPath) + add("--assume-yes") + }, workingDirectory = downloadedPackagePath ) return executeCommandLine(commandLine) diff --git a/src/main/kotlin/org/move/cli/runConfigurations/aptos/RunAptosCommandActionBase.kt b/src/main/kotlin/org/move/cli/runConfigurations/aptos/RunAptosCommandActionBase.kt index f9d11ddeb..1a95ff50d 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/aptos/RunAptosCommandActionBase.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/aptos/RunAptosCommandActionBase.kt @@ -8,9 +8,17 @@ package org.move.cli.runConfigurations.aptos import com.intellij.openapi.actionSystem.ActionUpdateThread import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.project.DumbAwareAction +import com.intellij.openapi.util.NlsActions.ActionDescription +import com.intellij.openapi.util.NlsActions.ActionText import org.move.cli.runConfigurations.hasMoveProject +import javax.swing.Icon + +abstract class RunAptosCommandActionBase( + @ActionText text: String? = null, + @ActionDescription description: String? = null, + icon: Icon? = null +): DumbAwareAction(text, description, icon) { -abstract class RunAptosCommandActionBase(text: String? = null) : DumbAwareAction(text) { override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT override fun update(e: AnActionEvent) { val hasMoveProject = e.project?.hasMoveProject == true diff --git a/src/main/kotlin/org/move/ide/notifications/Utils.kt b/src/main/kotlin/org/move/ide/notifications/Utils.kt index 5e827a43a..c1f6ca13c 100644 --- a/src/main/kotlin/org/move/ide/notifications/Utils.kt +++ b/src/main/kotlin/org/move/ide/notifications/Utils.kt @@ -33,6 +33,16 @@ fun Logger.logOrShowBalloon(@NotificationContent content: String, productionLeve } } +fun Project.showDebugBalloon( + @NotificationContent content: String, + type: NotificationType, + action: AnAction? = null +) { + if (isDebugModeEnabled()) { + showBalloon(content, type, action) + } +} + fun Project.showBalloon( @NotificationContent content: String, type: NotificationType, @@ -54,6 +64,17 @@ fun Project.showBalloon( Notifications.Bus.notify(notification, this) } +fun Project.showDebugBalloon( + @NotificationTitle title: String, + @NotificationContent content: String, + type: NotificationType, + action: AnAction? = null, +) { + if (isDebugModeEnabled()) { + this.showBalloon(title, content, type, action) + } +} + fun Component.showBalloon( @NotificationContent content: String, type: MessageType, diff --git a/src/main/resources/META-INF/intellij-move-core.xml b/src/main/resources/META-INF/intellij-move-core.xml index 249d71f36..5826a1a9b 100644 --- a/src/main/resources/META-INF/intellij-move-core.xml +++ b/src/main/resources/META-INF/intellij-move-core.xml @@ -438,6 +438,15 @@ class="org.move.ide.actions.MoveEditSettingsAction" icon="AllIcons.General.Settings">
+ + + + + + @@ -447,6 +456,8 @@ + + From 8f88f417a8bcb79f729faa536377f543d98738ff Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Wed, 22 May 2024 00:37:56 +0300 Subject: [PATCH 10/14] simplify ProgressManager calls --- .../org/move/bytecode/AptosBytecodeNotificationProvider.kt | 7 ++++--- .../kotlin/org/move/bytecode/DecompileAptosMvFileAction.kt | 4 +--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/org/move/bytecode/AptosBytecodeNotificationProvider.kt b/src/main/kotlin/org/move/bytecode/AptosBytecodeNotificationProvider.kt index 1ac342362..1e7297110 100644 --- a/src/main/kotlin/org/move/bytecode/AptosBytecodeNotificationProvider.kt +++ b/src/main/kotlin/org/move/bytecode/AptosBytecodeNotificationProvider.kt @@ -41,7 +41,6 @@ class AptosBytecodeNotificationProvider(project: Project): EditorNotificationPro return null } val properties = PropertiesComponent.getInstance(project) - val progressManager = ProgressManager.getInstance() val decompilationFailedKey = DECOMPILATION_FAILED + "." + file.path val aptosDecompiler = AptosBytecodeDecompiler() @@ -66,7 +65,7 @@ class AptosBytecodeNotificationProvider(project: Project): EditorNotificationPro if (decompilationFailed) { text = "Decompilation command failed" createActionLabel("Try again") { - val virtualFile = progressManager.run(decompilationTask) + val virtualFile = decompilationTask.runWithProgress() .unwrapOrElse { // something went wrong with the decompilation command again project.showDebugBalloon("Error with decompilation process", it, ERROR) @@ -79,7 +78,7 @@ class AptosBytecodeNotificationProvider(project: Project): EditorNotificationPro } } else { createActionLabel("Decompile into source code") { - val decompiledFile = progressManager.run(decompilationTask) + val decompiledFile = decompilationTask.runWithProgress() .unwrapOrElse { project.showDebugBalloon("Error with decompilation process", it, ERROR) return@unwrapOrElse null @@ -107,6 +106,8 @@ class AptosBytecodeNotificationProvider(project: Project): EditorNotificationPro val aptosDecompiler = AptosBytecodeDecompiler() return aptosDecompiler.decompileFileToTheSameDir(project, file) } + + fun runWithProgress(): RsResult = ProgressManager.getInstance().run(this) } companion object { diff --git a/src/main/kotlin/org/move/bytecode/DecompileAptosMvFileAction.kt b/src/main/kotlin/org/move/bytecode/DecompileAptosMvFileAction.kt index 18dab12db..9c0c442e3 100644 --- a/src/main/kotlin/org/move/bytecode/DecompileAptosMvFileAction.kt +++ b/src/main/kotlin/org/move/bytecode/DecompileAptosMvFileAction.kt @@ -3,7 +3,6 @@ package org.move.bytecode import com.intellij.notification.NotificationType.ERROR import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.actionSystem.CommonDataKeys -import com.intellij.openapi.progress.ProgressManager import com.intellij.openapi.project.DumbAwareAction import org.move.bytecode.AptosBytecodeNotificationProvider.DecompilationModalTask import org.move.cli.settings.getAptosCli @@ -17,8 +16,7 @@ class DecompileAptosMvFileAction: DumbAwareAction("Decompile .mv File", null, Mo override fun actionPerformed(e: AnActionEvent) { val project = e.project ?: return val file = e.getData(CommonDataKeys.PSI_FILE)?.virtualFile ?: return - val decompilationTask = DecompilationModalTask(project, file) - val decompiledFile = ProgressManager.getInstance().run(decompilationTask) + val decompiledFile = DecompilationModalTask(project, file).runWithProgress() .unwrapOrElse { project.showBalloon("Error with decompilation process", it, ERROR) return From afc29dc5b8bd600a1e2f985339233a699b286de2 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Wed, 22 May 2024 00:56:50 +0300 Subject: [PATCH 11/14] drop 232 --- .github/workflows/check.yml | 2 +- gradle-232.properties | 13 ------------- gradle.properties | 3 +-- 3 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 gradle-232.properties diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 3c1cf13ef..fe5b367e7 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -10,7 +10,7 @@ jobs: tests: strategy: matrix: - gradle-properties-version: [ 232, 233, 241 ] + gradle-properties-version: [ 233, 241 ] runs-on: ubuntu-latest env: diff --git a/gradle-232.properties b/gradle-232.properties deleted file mode 100644 index 1d08c2102..000000000 --- a/gradle-232.properties +++ /dev/null @@ -1,13 +0,0 @@ -# See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html -# for insight into build numbers and IntelliJ Platform versions. -pluginSinceBuild = 232 -pluginUntilBuild = 232.* - -# IntelliJ Platform Properties -> https://github.com/JetBrains/gradle-intellij-plugin#intellij-platform-properties -platformType = PC -platformVersion = 2023.2 - -# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html -# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22 -platformPlugins = org.toml.lang -verifierIdeVersions = PC-2023.2 diff --git a/gradle.properties b/gradle.properties index 1e6ab00fe..c1a5c37d8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,6 +17,5 @@ jbrVersion=17.0.7b1000.6 propertiesPluginEnvironmentNameProperty=shortPlatformVersion # properties files -# supported versions are 231, 232, 233, default is 231 # pass ORG_GRADLE_PROJECT_shortPlatformVersion environment variable to overwrite -shortPlatformVersion=232 +shortPlatformVersion=233 From 794b310422031dc288634b94c970a27460493dbf Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Wed, 22 May 2024 01:26:57 +0300 Subject: [PATCH 12/14] cleanup --- .../move/bytecode/FetchAptosPackageDialog.kt | 4 ---- .../CommandConfigurationBase.kt | 9 ++++----- src/main/kotlin/org/move/ide/MoveIcons.kt | 20 +++++++++++++------ .../ide/annotator/RsExternalLinterPass.kt | 6 +----- .../RsHighlightingPassFactoryRegistrar.kt | 1 - src/main/kotlin/org/move/openapiext/utils.kt | 2 +- 6 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/main/kotlin/org/move/bytecode/FetchAptosPackageDialog.kt b/src/main/kotlin/org/move/bytecode/FetchAptosPackageDialog.kt index 5269e63dc..0d83a8575 100644 --- a/src/main/kotlin/org/move/bytecode/FetchAptosPackageDialog.kt +++ b/src/main/kotlin/org/move/bytecode/FetchAptosPackageDialog.kt @@ -106,8 +106,4 @@ class FetchAptosPackageDialog(val project: Project): DialogWrapper(project, true } override fun getPreferredFocusedComponent(): JComponent = addressTextField - - override fun doValidate(): ValidationInfo? { - return super.doValidate() - } } \ No newline at end of file diff --git a/src/main/kotlin/org/move/cli/runConfigurations/CommandConfigurationBase.kt b/src/main/kotlin/org/move/cli/runConfigurations/CommandConfigurationBase.kt index 51a41f6d1..87a41a973 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/CommandConfigurationBase.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/CommandConfigurationBase.kt @@ -55,11 +55,10 @@ abstract class CommandConfigurationBase( return MoveCommandLineState(environment, config.cliPath, config.commandLine) } is CleanConfiguration.Err -> { - config.error.message?.let { - MvNotifications.pluginNotifications() - .createNotification("Run Configuration error", it, NotificationType.ERROR) - .notify(project) - } + val errorMessage = config.error.messageHtml.toString() + MvNotifications.pluginNotifications() + .createNotification("Run Configuration error", errorMessage, NotificationType.ERROR) + .notify(project) return null } } diff --git a/src/main/kotlin/org/move/ide/MoveIcons.kt b/src/main/kotlin/org/move/ide/MoveIcons.kt index 2bf229f9a..cdfea5a8b 100644 --- a/src/main/kotlin/org/move/ide/MoveIcons.kt +++ b/src/main/kotlin/org/move/ide/MoveIcons.kt @@ -4,8 +4,9 @@ import com.intellij.icons.AllIcons import com.intellij.openapi.util.IconLoader import com.intellij.ui.AnimatedIcon import com.intellij.ui.ColorUtil +import com.intellij.ui.JBColor import com.intellij.ui.LayeredIcon -import com.intellij.util.IconUtil +import com.intellij.ui.icons.RgbImageFilterSupplier import java.awt.Color import java.awt.Component import java.awt.Graphics @@ -57,7 +58,13 @@ object MoveIcons { val GEAR = load("/icons/gear.svg") val GEAR_OFF = load("/icons/gearOff.svg") - val GEAR_ANIMATED = AnimatedIcon(AnimatedIcon.Default.DELAY, GEAR, GEAR.rotated(15.0), GEAR.rotated(30.0), GEAR.rotated(45.0)) + val GEAR_ANIMATED = AnimatedIcon( + AnimatedIcon.Default.DELAY, + GEAR, + GEAR.rotated(15.0), + GEAR.rotated(30.0), + GEAR.rotated(45.0) + ) private fun load(path: String): Icon = IconLoader.getIcon(path, MoveIcons::class.java) } @@ -76,15 +83,16 @@ fun Icon.multiple(): Icon { return compoundIcon } +@Suppress("UnstableApiUsage") fun Icon.grayed(): Icon = - IconUtil.filterIcon(this, { - object : RGBImageFilter() { + IconLoader.filterIcon(this, object: RgbImageFilterSupplier { + override fun getFilter(): RGBImageFilter = object: RGBImageFilter() { override fun filterRGB(x: Int, y: Int, rgb: Int): Int { val color = Color(rgb, true) return ColorUtil.toAlpha(color, (color.alpha / 2.2).toInt()).rgb } } - }, null) + }) /** * Rotates the icon by the given angle, in degrees. @@ -96,7 +104,7 @@ fun Icon.grayed(): Icon = */ fun Icon.rotated(angle: Double): Icon { val q = this - return object : Icon by this { + return object: Icon by this { override fun paintIcon(c: Component, g: Graphics, x: Int, y: Int) { val g2d = g.create() as Graphics2D try { diff --git a/src/main/kotlin/org/move/ide/annotator/RsExternalLinterPass.kt b/src/main/kotlin/org/move/ide/annotator/RsExternalLinterPass.kt index 784137c95..fe1a1805c 100644 --- a/src/main/kotlin/org/move/ide/annotator/RsExternalLinterPass.kt +++ b/src/main/kotlin/org/move/ide/annotator/RsExternalLinterPass.kt @@ -3,7 +3,7 @@ * found in the LICENSE file. */ -package org.rust.ide.annotator +package org.move.ide.annotator import com.intellij.codeHighlighting.DirtyScopeTrackingHighlightingPassFactory import com.intellij.codeHighlighting.TextEditorHighlightingPass @@ -35,10 +35,6 @@ import org.move.cli.externalLinter.externalLinterSettings import org.move.cli.runConfigurations.aptos.AptosCompileArgs import org.move.cli.runConfigurations.aptos.workingDirectory import org.move.cli.settings.getAptosCli -import org.move.ide.annotator.RsExternalLinterResult -import org.move.ide.annotator.RsExternalLinterUtils -import org.move.ide.annotator.addHighlightsForFile -import org.move.ide.annotator.createDisposableOnAnyPsiChange import org.move.ide.notifications.RsExternalLinterSlowRunNotifier import org.move.lang.MoveFile import org.move.lang.core.psi.ext.findMoveProject diff --git a/src/main/kotlin/org/move/ide/annotator/RsHighlightingPassFactoryRegistrar.kt b/src/main/kotlin/org/move/ide/annotator/RsHighlightingPassFactoryRegistrar.kt index 2d59fee23..03fed1ff0 100644 --- a/src/main/kotlin/org/move/ide/annotator/RsHighlightingPassFactoryRegistrar.kt +++ b/src/main/kotlin/org/move/ide/annotator/RsHighlightingPassFactoryRegistrar.kt @@ -8,7 +8,6 @@ package org.move.ide.annotator import com.intellij.codeHighlighting.TextEditorHighlightingPassFactoryRegistrar import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar import com.intellij.openapi.project.Project -import org.rust.ide.annotator.RsExternalLinterPassFactory class RsHighlightingPassFactoryRegistrar : TextEditorHighlightingPassFactoryRegistrar { override fun registerHighlightingPassFactory(registrar: TextEditorHighlightingPassRegistrar, project: Project) { diff --git a/src/main/kotlin/org/move/openapiext/utils.kt b/src/main/kotlin/org/move/openapiext/utils.kt index 74ef282cb..1f61cbeb6 100644 --- a/src/main/kotlin/org/move/openapiext/utils.kt +++ b/src/main/kotlin/org/move/openapiext/utils.kt @@ -43,7 +43,7 @@ import org.jdom.Element import org.move.lang.toNioPathOrNull import org.move.openapiext.common.isHeadlessEnvironment import org.move.openapiext.common.isUnitTestMode -import org.rust.ide.annotator.RsExternalLinterPass +import org.move.ide.annotator.RsExternalLinterPass import java.nio.file.Path import java.nio.file.Paths import java.util.concurrent.Callable From 76565cbea27d3b2e0c633fe9487752772107c753 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Wed, 22 May 2024 01:39:37 +0300 Subject: [PATCH 13/14] fix deprecations --- .../cli/toolwindow/MoveProjectsTreeStructure.kt | 2 +- src/main/kotlin/org/move/ide/MoveIcons.kt | 8 +++++--- .../org/move/ide/annotator/RsExternalLinterUtils.kt | 2 +- .../kotlin/org/move/lang/core/psi/MvPsiManager.kt | 13 +++++++------ 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/org/move/cli/toolwindow/MoveProjectsTreeStructure.kt b/src/main/kotlin/org/move/cli/toolwindow/MoveProjectsTreeStructure.kt index fe0817523..a33d412fe 100644 --- a/src/main/kotlin/org/move/cli/toolwindow/MoveProjectsTreeStructure.kt +++ b/src/main/kotlin/org/move/cli/toolwindow/MoveProjectsTreeStructure.kt @@ -36,7 +36,7 @@ class MoveProjectsTreeStructure( fun updateMoveProjects(moveProjects: List) { this.moveProjects = moveProjects this.root = MoveSimpleNode.Root(moveProjects) - treeModel.invalidate() + treeModel.invalidateAsync() } sealed class MoveSimpleNode(parent: SimpleNode?) : CachingSimpleNode(parent) { diff --git a/src/main/kotlin/org/move/ide/MoveIcons.kt b/src/main/kotlin/org/move/ide/MoveIcons.kt index cdfea5a8b..5fa847084 100644 --- a/src/main/kotlin/org/move/ide/MoveIcons.kt +++ b/src/main/kotlin/org/move/ide/MoveIcons.kt @@ -6,7 +6,9 @@ import com.intellij.ui.AnimatedIcon import com.intellij.ui.ColorUtil import com.intellij.ui.JBColor import com.intellij.ui.LayeredIcon +import com.intellij.ui.LayeredIcon.Companion.layeredIcon import com.intellij.ui.icons.RgbImageFilterSupplier +import com.jetbrains.rd.generator.nova.array import java.awt.Color import java.awt.Component import java.awt.Graphics @@ -70,11 +72,11 @@ object MoveIcons { } -fun Icon.addFinalMark(): Icon = LayeredIcon(this, MoveIcons.FINAL_MARK) +fun Icon.addFinalMark(): Icon = layeredIcon(arrayOf(this, MoveIcons.FINAL_MARK)) -fun Icon.addStaticMark(): Icon = LayeredIcon(this, MoveIcons.STATIC_MARK) +fun Icon.addStaticMark(): Icon = layeredIcon(arrayOf(this, MoveIcons.STATIC_MARK)) -fun Icon.addTestMark(): Icon = LayeredIcon(this, MoveIcons.TEST_MARK) +fun Icon.addTestMark(): Icon = layeredIcon(arrayOf(this, MoveIcons.TEST_MARK)) fun Icon.multiple(): Icon { val compoundIcon = LayeredIcon(2) diff --git a/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt b/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt index 5de002f48..a7057d95e 100644 --- a/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt +++ b/src/main/kotlin/org/move/ide/annotator/RsExternalLinterUtils.kt @@ -23,7 +23,7 @@ import com.intellij.psi.util.PsiModificationTracker import com.intellij.util.PathUtil import com.intellij.util.io.URLUtil import com.intellij.util.messages.MessageBus -import org.apache.commons.lang3.StringEscapeUtils +import org.apache.commons.text.StringEscapeUtils import org.jetbrains.annotations.Nls import org.move.cli.externalLinter.RsExternalLinterWidget import org.move.cli.externalLinter.externalLinterSettings diff --git a/src/main/kotlin/org/move/lang/core/psi/MvPsiManager.kt b/src/main/kotlin/org/move/lang/core/psi/MvPsiManager.kt index 9a720418d..f22aa89f2 100644 --- a/src/main/kotlin/org/move/lang/core/psi/MvPsiManager.kt +++ b/src/main/kotlin/org/move/lang/core/psi/MvPsiManager.kt @@ -5,7 +5,6 @@ package org.move.lang.core.psi -import com.intellij.ProjectTopics import com.intellij.injected.editor.VirtualFileWindow import com.intellij.openapi.Disposable import com.intellij.openapi.components.service @@ -95,7 +94,7 @@ interface MovePsiChangeListener { fun movePsiChanged(file: PsiFile, element: PsiElement, isStructureModification: Boolean) } -class MvPsiManagerImpl(val project: Project) : MvPsiManager, Disposable { +class MvPsiManagerImpl(val project: Project): MvPsiManager, Disposable { override val moveStructureModificationTracker = SimpleModificationTracker() @@ -103,7 +102,7 @@ class MvPsiManagerImpl(val project: Project) : MvPsiManager, Disposable { PsiManager.getInstance(project).addPsiTreeChangeListener(CacheInvalidator(), this) project.messageBus.connect() - .subscribe(ProjectTopics.PROJECT_ROOTS, object : ModuleRootListener { + .subscribe(ModuleRootListener.TOPIC, object: ModuleRootListener { override fun rootsChanged(event: ModuleRootEvent) { incStructureModificationCount() } @@ -116,7 +115,7 @@ class MvPsiManagerImpl(val project: Project) : MvPsiManager, Disposable { override fun dispose() {} - inner class CacheInvalidator : MvPsiTreeChangeAdapter() { + inner class CacheInvalidator: MvPsiTreeChangeAdapter() { override fun handleEvent(event: MvPsiTreeChangeEvent) { val element = when (event) { is ChildRemoval.Before -> event.child @@ -187,7 +186,8 @@ class MvPsiManagerImpl(val project: Project) : MvPsiManager, Disposable { // about it because it is a rare case and implementing it differently // is much more difficult. - val owner = if (DumbService.isDumb(project)) null else psi.findModificationTrackerOwner(!isChildrenChange) + val owner = + if (DumbService.isDumb(project)) null else psi.findModificationTrackerOwner(!isChildrenChange) // Whitespace/comment changes are meaningful for macros only // (b/c they affect range mappings and body hashes) @@ -204,7 +204,8 @@ class MvPsiManagerImpl(val project: Project) : MvPsiManager, Disposable { if (isStructureModification) { incRustStructureModificationCount(file, psi) } - project.messageBus.syncPublisher(MOVE_PSI_CHANGE_TOPIC).movePsiChanged(file, psi, isStructureModification) + project.messageBus.syncPublisher(MOVE_PSI_CHANGE_TOPIC) + .movePsiChanged(file, psi, isStructureModification) } // private val isMacroExpansionModeNew From 604a327c6f09c7267c15d1c8af9f6f3d7974cdf3 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Thu, 23 May 2024 15:04:11 +0300 Subject: [PATCH 14/14] disable deletion test --- .../org/move/cli/MoveProjectsService.kt | 16 +++-- .../org/move/utils/tests/MvProjectTestBase.kt | 4 +- .../MoveExternalSystemProjectAwareTest.kt | 72 +++++++++---------- 3 files changed, 48 insertions(+), 44 deletions(-) diff --git a/src/main/kotlin/org/move/cli/MoveProjectsService.kt b/src/main/kotlin/org/move/cli/MoveProjectsService.kt index 5ee620121..7bf48082d 100644 --- a/src/main/kotlin/org/move/cli/MoveProjectsService.kt +++ b/src/main/kotlin/org/move/cli/MoveProjectsService.kt @@ -7,10 +7,8 @@ import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.components.service import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.logger -import com.intellij.openapi.externalSystem.autoimport.AutoImportProjectNotificationAware import com.intellij.openapi.externalSystem.autoimport.AutoImportProjectTracker import com.intellij.openapi.externalSystem.autoimport.ExternalSystemProjectTracker -import com.intellij.openapi.externalSystem.autoimport.changes.FilesChangesListener import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.project.Project @@ -30,6 +28,7 @@ import com.intellij.psi.PsiFile import com.intellij.psi.PsiInvalidElementAccessException import com.intellij.psi.util.parents import com.intellij.util.messages.Topic +import org.jetbrains.annotations.TestOnly import org.move.cli.externalSystem.MoveExternalSystemProjectAware import org.move.cli.settings.MvProjectSettingsServiceBase.* import org.move.cli.settings.MvProjectSettingsServiceBase.Companion.MOVE_SETTINGS_TOPIC @@ -48,6 +47,7 @@ import java.io.File import java.nio.file.Path import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletionException +import java.util.concurrent.TimeUnit val Project.moveProjectsService get() = service() @@ -67,13 +67,15 @@ class MoveProjectsService(val project: Project): Disposable { fun scheduleProjectsRefresh(reason: String? = null): CompletableFuture> { LOG.logOrShowBalloon("Refresh Projects ($reason)") - val moveProjectsFut = - modifyProjectModel { - doRefreshProjects(project, reason) - } - return moveProjectsFut + return modifyProjectModel { + doRefreshProjects(project, reason) + } } + @TestOnly + fun scheduleProjectsRefreshSync(reason: String? = null): List = + scheduleProjectsRefresh(reason).get(1, TimeUnit.MINUTES) + private fun registerProjectAware(project: Project, disposable: Disposable) { // There is no sense to register `CargoExternalSystemProjectAware` for default project. // Moreover, it may break searchable options building. diff --git a/src/main/kotlin/org/move/utils/tests/MvProjectTestBase.kt b/src/main/kotlin/org/move/utils/tests/MvProjectTestBase.kt index 429cf9d58..1c1b58e87 100644 --- a/src/main/kotlin/org/move/utils/tests/MvProjectTestBase.kt +++ b/src/main/kotlin/org/move/utils/tests/MvProjectTestBase.kt @@ -22,6 +22,7 @@ import org.move.openapiext.toPsiDirectory import org.move.openapiext.toPsiFile import org.move.openapiext.toVirtualFile import org.move.utils.tests.base.TestCase +import java.util.concurrent.TimeUnit @TestOnly fun setRegistryKey(key: String, value: Boolean) = Registry.get(key).setValue(value) @@ -71,7 +72,8 @@ abstract class MvProjectTestBase: CodeInsightFixtureTestCase