From 9795f3da1f4eb96af65936d67fe272f7022611ce Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Sat, 9 Nov 2024 14:21:04 +0100 Subject: [PATCH 1/2] remove dependency balloon notification --- .../org/move/cli/MoveProjectsSyncTask.kt | 21 ++++++++++--------- .../org/move/ide/notifications/Utils.kt | 18 ---------------- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/src/main/kotlin/org/move/cli/MoveProjectsSyncTask.kt b/src/main/kotlin/org/move/cli/MoveProjectsSyncTask.kt index 12ecb5ac3..47ec18380 100644 --- a/src/main/kotlin/org/move/cli/MoveProjectsSyncTask.kt +++ b/src/main/kotlin/org/move/cli/MoveProjectsSyncTask.kt @@ -36,6 +36,7 @@ import org.move.cli.manifest.MoveToml import org.move.cli.settings.getAptosCli import org.move.cli.settings.moveSettings import org.move.ide.notifications.showBalloon +import org.move.ide.notifications.showDebugBalloon import org.move.lang.toNioPathOrNull import org.move.lang.toTomlFile import org.move.openapiext.TaskResult @@ -298,16 +299,16 @@ class MoveProjectsSyncTask( parsedDeps = parsedDeps.withExtended(rootMoveToml.dev_deps) } for ((dep, addressMap) in parsedDeps) { - val depRoot = dep.rootDirectory() - if (depRoot == null) { - // root does not exist - project.showBalloon( - "Cannot resolve the ${dep.name.quote()} dependency. " + - "Root directory does not exist.", - ERROR - ) - continue - } + val depRoot = dep.rootDirectory() ?: continue +// if (depRoot == null) { +// // root does not exist +// project.showDebugBalloon( +// title = "Cannot resolve the ${dep.name.quote()} dependency.", +// content = "Root directory does not exist.", +// ERROR +// ) +// continue +// } val depId = DepId(depRoot.path) if (depId in visitedIds) continue diff --git a/src/main/kotlin/org/move/ide/notifications/Utils.kt b/src/main/kotlin/org/move/ide/notifications/Utils.kt index c1f6ca13c..102774f7c 100644 --- a/src/main/kotlin/org/move/ide/notifications/Utils.kt +++ b/src/main/kotlin/org/move/ide/notifications/Utils.kt @@ -33,24 +33,6 @@ 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, - action: AnAction? = null -) { - showBalloon("", content, type, action) -} - fun Project.showBalloon( @NotificationTitle title: String, @NotificationContent content: String, From 765dbea697f356eb657ac93b83377ee918499e0b Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Sat, 9 Nov 2024 18:08:13 +0100 Subject: [PATCH 2/2] proper command line output for Build/Sync task --- .../org/move/cli/MoveProjectsSyncTask.kt | 140 +++++++++++------- .../org/move/cli/manifest/TomlDependency.kt | 40 +++-- .../move/cli/runConfigurations/aptos/Aptos.kt | 26 ++-- src/main/kotlin/org/move/stdext/RsResult.kt | 5 + .../MoveTomlUnresolvedPathInspectionTest.kt | 32 ++++ .../kotlin/org/move/utils/tests/FileTree.kt | 9 +- 6 files changed, 171 insertions(+), 81 deletions(-) create mode 100644 src/test/kotlin/org/move/toml/MoveTomlUnresolvedPathInspectionTest.kt diff --git a/src/main/kotlin/org/move/cli/MoveProjectsSyncTask.kt b/src/main/kotlin/org/move/cli/MoveProjectsSyncTask.kt index 47ec18380..cde4a5b40 100644 --- a/src/main/kotlin/org/move/cli/MoveProjectsSyncTask.kt +++ b/src/main/kotlin/org/move/cli/MoveProjectsSyncTask.kt @@ -10,10 +10,8 @@ import com.intellij.build.events.BuildEventsNls import com.intellij.build.events.MessageEvent import com.intellij.build.progress.BuildProgress 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.notification.NotificationType.ERROR import com.intellij.openapi.Disposable import com.intellij.openapi.actionSystem.ActionManager import com.intellij.openapi.actionSystem.ActionUpdateThread @@ -30,13 +28,10 @@ import com.intellij.openapi.util.Key import com.intellij.openapi.util.NlsContexts import com.intellij.openapi.vfs.VirtualFile import com.intellij.util.concurrency.annotations.RequiresReadLock -import io.ktor.http.* import org.move.cli.MoveProject.UpdateStatus import org.move.cli.manifest.MoveToml import org.move.cli.settings.getAptosCli import org.move.cli.settings.moveSettings -import org.move.ide.notifications.showBalloon -import org.move.ide.notifications.showDebugBalloon import org.move.lang.toNioPathOrNull import org.move.lang.toTomlFile import org.move.openapiext.TaskResult @@ -142,17 +137,27 @@ class MoveProjectsSyncTask( runReadAction { val tomlFile = moveTomlFile.toTomlFile(project)!! val rootMoveToml = MoveToml.fromTomlFile(tomlFile) -// val rootMoveToml = MoveToml.fromTomlFile(tomlFile, projectRoot) val rootPackage = MovePackage.fromMoveToml(rootMoveToml) val rootProject = MoveProject(project, rootPackage, emptyList()) rootProject to rootMoveToml } - val result = fetchDependencyPackages(context, projectRoot) - if (result is TaskResult.Err) { - moveProject = - moveProject.copy(fetchDepsStatus = UpdateStatus.UpdateFailed("Failed to fetch dependency packages")) + if (!project.moveSettings.fetchAptosDeps) { + context.syncProgress.info( + "Fetching dependencies (disabled)", + "Fetching dependencies disabled in the plugin settings", + ) + } else { + context.runWithChildProgress("Fetching dependencies") { childContext -> + val result = fetchDependencyPackages(childContext, projectRoot) + if (result is TaskResult.Err) { + moveProject = + moveProject.copy(fetchDepsStatus = UpdateStatus.UpdateFailed("Failed to fetch dependency packages")) + } + result + } } + context.checkCanceled() val deps = (context.runWithChildProgress("Loading dependencies") { childContext -> @@ -167,7 +172,7 @@ class MoveProjectsSyncTask( deps, visitedDepIds, true, - childContext.progress + childContext ) TaskResult.Ok(deps) } @@ -176,30 +181,31 @@ class MoveProjectsSyncTask( projects.add(moveProject.copy(dependencies = deps)) } - private fun fetchDependencyPackages(context: SyncContext, projectRoot: Path): TaskResult = - context.runWithChildProgress("Synchronize dependencies") { childContext -> - val listener = SyncProcessAdapter(childContext) - - val skipLatest = project.moveSettings.skipFetchLatestGitDeps - val aptos = project.getAptosCli(parentDisposable = this) - when { - aptos == null -> TaskResult.Err("Invalid Aptos CLI configuration") - else -> { - aptos.fetchPackageDependencies( - project, - projectRoot, - skipLatest, - processListener = listener - ).unwrapOrElse { - return@runWithChildProgress TaskResult.Err( - "Failed to fetch / update dependencies", - it.message - ) - } - TaskResult.Ok(Unit) + private fun fetchDependencyPackages(childContext: SyncContext, projectRoot: Path): TaskResult { + val taskResult: TaskResult? = null + val syncListener = + SyncProcessListener(childContext) { event -> + childContext.syncProgress.output(event.text, true) + } + val skipLatest = project.moveSettings.skipFetchLatestGitDeps + val aptos = project.getAptosCli(parentDisposable = this) + return when { + aptos == null -> TaskResult.Err("Invalid Aptos CLI configuration") + else -> { + aptos.fetchPackageDependencies( + projectRoot, + skipLatest, + processListener = syncListener + ).unwrapOrElse { + return TaskResult.Err( + "Failed to fetch / update dependencies", + it.message + ) } + TaskResult.Ok(Unit) } } + } private fun createSyncProgressDescriptor(progress: ProgressIndicator): BuildProgressDescriptor { val buildContentDescriptor = BuildContentDescriptor( @@ -244,6 +250,8 @@ class MoveProjectsSyncTask( val id: Any get() = syncProgress.id + fun checkCanceled() = progress.checkCanceled() + fun runWithChildProgress( @NlsContexts.ProgressText title: String, action: (SyncContext) -> TaskResult @@ -289,26 +297,26 @@ class MoveProjectsSyncTask( deps: MutableList>, visitedIds: MutableSet, isRoot: Boolean, - progress: ProgressIndicator + syncContext: SyncContext, ) { // checks for the cancel() of the whole SyncTask - progress.checkCanceled() + syncContext.checkCanceled() var parsedDeps = rootMoveToml.deps if (isRoot) { parsedDeps = parsedDeps.withExtended(rootMoveToml.dev_deps) } for ((dep, addressMap) in parsedDeps) { - val depRoot = dep.rootDirectory() ?: continue -// if (depRoot == null) { -// // root does not exist -// project.showDebugBalloon( -// title = "Cannot resolve the ${dep.name.quote()} dependency.", -// content = "Root directory does not exist.", -// ERROR -// ) -// continue -// } + val depRoot = dep.rootDirectory() + .unwrapOrElse { + syncContext.syncProgress.message( + "Failed to load ${dep.name}.", + "Error when resolving dependency root, \n${it.message}", + MessageEvent.Kind.ERROR, + null + ) + null + } ?: continue val depId = DepId(depRoot.path) if (depId in visitedIds) continue @@ -322,19 +330,28 @@ class MoveProjectsSyncTask( // parse all nested dependencies with their address maps visitedIds.add(depId) - loadDependencies(project, depMoveToml, deps, visitedIds, false, progress) - + loadDependencies(project, depMoveToml, deps, visitedIds, false, syncContext) deps.add(Pair(depPackage, addressMap)) + + syncContext.syncProgress.output( + "${dep.name} dependency loaded successfully, " + + "\npackage directory: \n${depRoot.presentableUrl}", + true, + ) + syncContext.syncProgress.newline() + syncContext.syncProgress.newline() } } } } -private class SyncProcessAdapter( - private val context: MoveProjectsSyncTask.SyncContext -): ProcessAdapter(), - ProcessProgressListener { +private class SyncProcessListener( + private val context: MoveProjectsSyncTask.SyncContext, + private val onTextAvailable: (ProcessEvent) -> Unit, +): ProcessProgressListener { override fun onTextAvailable(event: ProcessEvent, outputType: Key) { + onTextAvailable(event) + // show progress bars for the long operations val text = event.text.trim { it <= ' ' } if (text.startsWith("FETCHING GIT DEPENDENCY")) { val gitRepo = text.substring("FETCHING GIT DEPENDENCY".length) @@ -346,8 +363,8 @@ private class SyncProcessAdapter( } } - override fun error(title: String, message: String) = context.error(title, message) - override fun warning(title: String, message: String) = context.warning(title, message) + override fun error(title: String, message: String) = context.syncProgress.error(title, message) + override fun warning(title: String, message: String) = context.syncProgress.warning(title, message) } private fun BuildProgress.runWithChildProgress( @@ -372,18 +389,29 @@ private fun BuildProgress.runWithChildProgress( } } -private fun MoveProjectsSyncTask.SyncContext.error( +private fun BuildProgress.error( + @BuildEventsNls.Title title: String, + @BuildEventsNls.Message message: String +) { + message(title, message, MessageEvent.Kind.ERROR, null) +} + +private fun BuildProgress.warning( @BuildEventsNls.Title title: String, @BuildEventsNls.Message message: String ) { - syncProgress.message(title, message, com.intellij.build.events.MessageEvent.Kind.ERROR, null) + message(title, message, MessageEvent.Kind.WARNING, null) } -private fun MoveProjectsSyncTask.SyncContext.warning( +private fun BuildProgress.info( @BuildEventsNls.Title title: String, @BuildEventsNls.Message message: String ) { - syncProgress.message(title, message, com.intellij.build.events.MessageEvent.Kind.WARNING, null) + message(title, message, MessageEvent.Kind.INFO, null) +} + +private fun BuildProgress.newline() { + output("\n", true) } diff --git a/src/main/kotlin/org/move/cli/manifest/TomlDependency.kt b/src/main/kotlin/org/move/cli/manifest/TomlDependency.kt index 1df46af7e..00809f47e 100644 --- a/src/main/kotlin/org/move/cli/manifest/TomlDependency.kt +++ b/src/main/kotlin/org/move/cli/manifest/TomlDependency.kt @@ -3,22 +3,34 @@ package org.move.cli.manifest import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.findDirectory +import com.intellij.util.SystemProperties import com.intellij.util.concurrency.annotations.RequiresReadLock +import org.move.stdext.RsResult +import org.move.stdext.RsResult.Err +import org.move.stdext.RsResult.Ok +import org.move.stdext.exists +import org.move.stdext.toPath import java.nio.file.Path sealed class TomlDependency { abstract val name: String @RequiresReadLock - abstract fun rootDirectory(): VirtualFile? + abstract fun rootDirectory(): RsResult data class Local( override val name: String, private val localPath: Path, - ) : TomlDependency() { + ): TomlDependency() { @RequiresReadLock - override fun rootDirectory(): VirtualFile? = VfsUtil.findFile(localPath, true) + override fun rootDirectory(): RsResult { + val vFile = VfsUtil.findFile(localPath, true) + if (vFile == null) { + return Err(DependencyError("Cannot find dependency folder: $localPath")) + } + return Ok(vFile) + } } data class Git( @@ -26,17 +38,25 @@ sealed class TomlDependency { private val repo: String, private val rev: String, private val subdir: String, - ) : TomlDependency() { + ): TomlDependency() { @RequiresReadLock - override fun rootDirectory(): VirtualFile? { - val userHome = VfsUtil.getUserHomeDir() ?: return null - val sourceDirName = dirNameAptos(repo, rev) - return userHome.findDirectory(".move/$sourceDirName/$subdir") + override fun rootDirectory(): RsResult { + val moveHomePath = SystemProperties.getUserHome().toPath().resolve(".move") + if (!moveHomePath.exists()) { + return Err(DependencyError("$moveHomePath directory does not exist")) + } + val depDirName = dependencyDirName(repo, rev) + val depRoot = moveHomePath.resolve(depDirName).resolve(subdir) + val depRootFile = VfsUtil.findFile(depRoot, true) + if (depRootFile == null) { + return Err(DependencyError("cannot find folder: $depRoot")) + } + return Ok(depRootFile) } companion object { - fun dirNameAptos(repo: String, rev: String): String { + fun dependencyDirName(repo: String, rev: String): String { val sanitizedRepoName = repo.replace(Regex("[/:.@]"), "_") val aptosRevName = rev.replace("/", "_") return "${sanitizedRepoName}_$aptosRevName" @@ -44,3 +64,5 @@ sealed class TomlDependency { } } } + +data class DependencyError(val message: String) 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 7bb111811..4e2d462ef 100644 --- a/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt +++ b/src/main/kotlin/org/move/cli/runConfigurations/aptos/Aptos.kt @@ -63,25 +63,21 @@ data class Aptos(val cliLocation: Path, val parentDisposable: Disposable?): Disp } fun fetchPackageDependencies( - project: Project, projectDir: Path, skipLatest: Boolean, processListener: ProcessListener ): RsProcessResult { - if (project.moveSettings.fetchAptosDeps) { - val commandLine = - AptosCommandLine( - subCommand = "move compile", - arguments = listOfNotNull( - "--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 - executeCommandLine(commandLine, listener = processListener) -// .unwrapOrElse { return Err(it) } - } + val commandLine = + AptosCommandLine( + subCommand = "move compile", + arguments = listOfNotNull( + "--skip-fetch-latest-git-deps".takeIf { skipLatest } + ), + workingDirectory = projectDir + ) + commandLine + .toColoredCommandLine(this.cliLocation) + .execute(innerDisposable, listener = processListener) return Ok(Unit) } diff --git a/src/main/kotlin/org/move/stdext/RsResult.kt b/src/main/kotlin/org/move/stdext/RsResult.kt index 5ad35bd3e..0c68dd300 100644 --- a/src/main/kotlin/org/move/stdext/RsResult.kt +++ b/src/main/kotlin/org/move/stdext/RsResult.kt @@ -41,6 +41,11 @@ sealed class RsResult { } } + fun unwrapErr(): E = when (this) { + is Err -> err + is Ok -> throw IllegalStateException("called `RsResult.unwrapErr()` on an `Ok` value") + } + fun unwrapOrNull(): T? = when (this) { is Ok -> ok is Err -> null diff --git a/src/test/kotlin/org/move/toml/MoveTomlUnresolvedPathInspectionTest.kt b/src/test/kotlin/org/move/toml/MoveTomlUnresolvedPathInspectionTest.kt new file mode 100644 index 000000000..91897bf89 --- /dev/null +++ b/src/test/kotlin/org/move/toml/MoveTomlUnresolvedPathInspectionTest.kt @@ -0,0 +1,32 @@ +package org.move.toml + +import org.move.utils.tests.annotation.InspectionProjectTestBase +import org.toml.ide.inspections.TomlUnresolvedReferenceInspection + +class MoveTomlUnresolvedPathInspectionTest: + InspectionProjectTestBase(TomlUnresolvedReferenceInspection::class) { + + fun `test local dependency found`() = checkByFileTree(code = { + namedMoveToml("Root", """ + [dependencies] + local = "./child" + + + """) + sources {} + dir("child") { + namedMoveToml("Child") + sources { } + } + }) + + fun `test local dependency not found`() = checkByFileTree(code = { + namedMoveToml("Root", """ + [dependencies] + local = "./child" + + + """) + sources {} + }) +} \ No newline at end of file diff --git a/src/test/kotlin/org/move/utils/tests/FileTree.kt b/src/test/kotlin/org/move/utils/tests/FileTree.kt index 7c4e76678..0c753e720 100644 --- a/src/test/kotlin/org/move/utils/tests/FileTree.kt +++ b/src/test/kotlin/org/move/utils/tests/FileTree.kt @@ -84,9 +84,16 @@ interface FileTreeBuilder { name = "$packageName" """ ) + fun namedMoveToml(packageName: String, @Language("TOML") code: String) = moveToml( + """ + [package] + name = "packageName" + + $code + """) fun git(repo: String, rev: String, builder: TreeBuilder = {}) { - val dirName = TomlDependency.Git.dirNameAptos(repo, rev) + val dirName = TomlDependency.Git.dependencyDirName(repo, rev) return dir(dirName, builder) }