Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve integration with aptos-cli #144

Merged
merged 10 commits into from
May 6, 2024
8 changes: 6 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ version = pluginVersion
plugins {
id("java")
kotlin("jvm") version "1.9.22"
id("org.jetbrains.intellij") version "1.17.2"
id("org.jetbrains.intellij") version "1.17.3"
id("org.jetbrains.grammarkit") version "2022.3.2.2"
id("net.saliman.properties") version "1.5.2"
id("org.gradle.idea")
Expand Down Expand Up @@ -264,7 +264,11 @@ project(":plugin") {
channels.set(listOf(publishingChannel))
}

runIde { enabled = true }
runIde {
enabled = true
systemProperty("org.move.debug.enabled", true)
// systemProperty("idea.log.debug.categories", "org.move.cli")
}
prepareSandbox { enabled = true }
buildSearchableOptions {
enabled = true
Expand Down
15 changes: 15 additions & 0 deletions src/main/kotlin/org/move/cli/MoveProject.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.move.cli

import com.intellij.openapi.project.Project
import com.intellij.openapi.util.NlsContexts.Tooltip
import com.intellij.openapi.util.UserDataHolderBase
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.PsiFileFactory
Expand Down Expand Up @@ -31,6 +32,8 @@ data class MoveProject(
val project: Project,
val currentPackage: MovePackage,
val dependencies: List<Pair<MovePackage, RawAddressMap>>,
// updates
val fetchDepsStatus: UpdateStatus = UpdateStatus.NeedsUpdate,
): UserDataHolderBase() {

val contentRoot: VirtualFile get() = this.currentPackage.contentRoot
Expand Down Expand Up @@ -130,6 +133,18 @@ data class MoveProject(
}
}

sealed class UpdateStatus(private val priority: Int) {
// object UpToDate : UpdateStatus(0)
object NeedsUpdate : UpdateStatus(1)
class UpdateFailed(@Tooltip val reason: String) : UpdateStatus(2) {
override fun toString(): String = reason
}
fun merge(status: UpdateStatus): UpdateStatus = if (priority >= status.priority) this else status
}

val mergedStatus: UpdateStatus get() = fetchDepsStatus
// val mergedStatus: UpdateStatus get() = fetchDepsStatus.merge(stdlibStatus)

override fun toString(): String {
return "MoveProject(" +
"root=${this.contentRoot.path}, " +
Expand Down
12 changes: 10 additions & 2 deletions src/main/kotlin/org/move/cli/MoveProjectsService.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.move.cli

import com.intellij.execution.RunManager
import com.intellij.notification.NotificationType.INFORMATION
import com.intellij.openapi.Disposable
import com.intellij.openapi.application.invokeAndWaitIfNeeded
import com.intellij.openapi.application.runWriteAction
Expand Down Expand Up @@ -30,6 +31,8 @@ import org.move.cli.externalSystem.MoveExternalSystemProjectAware
import org.move.cli.settings.MvProjectSettingsServiceBase.*
import org.move.cli.settings.MvProjectSettingsServiceBase.Companion.MOVE_SETTINGS_TOPIC
import org.move.cli.settings.debugErrorOrFallback
import org.move.cli.settings.isDebugModeEnabled
import org.move.ide.notifications.showBalloon
import org.move.lang.core.psi.ext.elementType
import org.move.lang.toNioPathOrNull
import org.move.openapiext.checkReadAccessAllowed
Expand Down Expand Up @@ -65,6 +68,9 @@ class MoveProjectsService(val project: Project): Disposable {

fun scheduleProjectsRefresh(reason: String? = null): CompletableFuture<List<MoveProject>> {
LOG.logProjectsRefresh("scheduled", reason)
if (isDebugModeEnabled()) {
project.showBalloon("Refresh Projects ($reason)", INFORMATION)
}
val moveProjectsFut =
modifyProjectModel {
doRefreshProjects(project, reason)
Expand All @@ -82,7 +88,9 @@ class MoveProjectsService(val project: Project): Disposable {

val moveProjectAware = MoveExternalSystemProjectAware(project)
val projectTracker = ExternalSystemProjectTracker.getInstance(project)
// starts tracking of project settings files
projectTracker.register(moveProjectAware, disposable)
// activate auto-reload
projectTracker.activate(moveProjectAware.projectId)

@Suppress("UnstableApiUsage")
Expand Down Expand Up @@ -112,7 +120,7 @@ class MoveProjectsService(val project: Project): Disposable {
} catch (e: PsiInvalidElementAccessException) {
val parentsChain =
psiElement.parents(true).map { it.elementType }.joinToString(" -> ")
project.debugErrorOrFallback(
debugErrorOrFallback(
"Cannot get the containing file for the ${psiElement.javaClass.name}, " +
"elementType is ${psiElement.elementType}, parents chain is $parentsChain",
cause = e
Expand Down Expand Up @@ -199,7 +207,7 @@ class MoveProjectsService(val project: Project): Disposable {
modifyProjects: (List<MoveProject>) -> CompletableFuture<List<MoveProject>>
): CompletableFuture<List<MoveProject>> {
val refreshStatusPublisher =
project.messageBus.syncPublisher(MoveProjectsService.MOVE_PROJECTS_REFRESH_TOPIC)
project.messageBus.syncPublisher(MOVE_PROJECTS_REFRESH_TOPIC)

val wrappedModifyProjects = { projects: List<MoveProject> ->
refreshStatusPublisher.onRefreshStarted()
Expand Down
132 changes: 70 additions & 62 deletions src/main/kotlin/org/move/cli/MoveProjectsSyncTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ 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.openapi.actionSystem.ActionManager
import com.intellij.openapi.actionSystem.AnActionEvent
Expand All @@ -21,19 +22,17 @@ 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.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.APTOS
import org.move.cli.settings.Blockchain.SUI
import org.move.cli.settings.blockchain
import org.move.cli.settings.suiCli
import org.move.cli.settings.blockchainCli
import org.move.cli.settings.moveSettings
import org.move.lang.toNioPathOrNull
import org.move.lang.toTomlFile
import org.move.openapiext.TaskResult
import org.move.openapiext.contentRoots
import org.move.openapiext.resolveExisting
import org.move.openapiext.toVirtualFile
import org.move.openapiext.*
import org.move.stdext.iterateFiles
import org.move.stdext.unwrapOrElse
import org.move.stdext.withExtended
Expand All @@ -60,14 +59,13 @@ class MoveProjectsSyncTask(

val refreshedProjects = doRun(indicator, syncProgress)

// TODO:
// val isUpdateFailed = refreshedProjects.any { it.mergedStatus is CargoProject.UpdateStatus.UpdateFailed }
// if (isUpdateFailed) {
// syncProgress.fail()
// } else {
// syncProgress.finish()
// }
syncProgress.finish()
val isUpdateFailed =
refreshedProjects.any { it.mergedStatus is UpdateStatus.UpdateFailed }
if (isUpdateFailed) {
syncProgress.fail()
} else {
syncProgress.finish()
}

refreshedProjects
} catch (e: Throwable) {
Expand Down Expand Up @@ -120,55 +118,68 @@ class MoveProjectsSyncTask(
context: SyncContext
) {
val projectRoot = moveTomlFile.parent?.toNioPathOrNull() ?: error("cannot be invalid path")
var moveProject =
runReadAction {
val tomlFile = moveTomlFile.toTomlFile(project)!!
val moveToml = MoveToml.fromTomlFile(tomlFile, projectRoot)
val rootPackage = MovePackage.fromMoveToml(moveToml)
MoveProject(project, rootPackage, emptyList())
}

context.runWithChildProgress("Fetching dependency packages") { childContext ->
fetchProjectDependencies(
projectRoot, listener = SyncProcessAdapter(childContext)
)
TaskResult.Ok(Unit)
val result = fetchDependencyPackages(context, projectRoot)
if (result is TaskResult.Err) {
moveProject =
moveProject.copy(fetchDepsStatus = UpdateStatus.UpdateFailed("Failed to fetch dependency packages"))
}

val (rootPackage, deps) =
val deps =
(context.runWithChildProgress("Loading dependencies") { childContext ->
// Blocks till completed or cancelled by the toml / file change
runReadAction {
val tomlFile = moveTomlFile.toTomlFile(project)!!
val moveToml = MoveToml.fromTomlFile(tomlFile, projectRoot)
val rootPackage = MovePackage.fromMoveToml(moveToml)

val rootPackage = moveProject.currentPackage
val deps = mutableListOf<Pair<MovePackage, RawAddressMap>>()
val visitedDepIds = mutableSetOf(DepId(rootPackage.contentRoot.path))
loadDependencies(project, moveToml, deps, visitedDepIds, true, childContext.progress)

TaskResult.Ok(Pair(rootPackage, deps))
loadDependencies(
project,
rootPackage.moveToml,
deps,
visitedDepIds,
true,
childContext.progress
)
TaskResult.Ok(deps)
}
} as TaskResult.Ok).value

val moveProject = MoveProject(project, rootPackage, deps)
projects.add(moveProject)
projects.add(moveProject.copy(dependencies = deps))
}

private fun fetchProjectDependencies(projectDir: Path, listener: ProcessProgressListener) {
when (project.blockchain) {
SUI -> {
val suiCli = project.suiCli
if (suiCli == null) {
listener.error("Invalid Sui CLI configuration", "")
return
private fun fetchDependencyPackages(context: SyncContext, projectRoot: Path): TaskResult<Unit> =
context.runWithChildProgress("Synchronize dependencies") { childContext ->
val listener = SyncProcessAdapter(childContext)

val blockchain = project.blockchain
val skipLatest = project.moveSettings.skipFetchLatestGitDeps
val blockchainCli = project.blockchainCli
when {
blockchainCli == null -> TaskResult.Err("Invalid $blockchain CLI configuration")
else -> {
blockchainCli
.fetchPackageDependencies(
projectRoot,
skipLatest,
owner = project.rootDisposable,
processListener = listener
).unwrapOrElse {
return@runWithChildProgress TaskResult.Err(
"Failed to fetch / update dependencies",
it.message
)
}
TaskResult.Ok(Unit)
}
suiCli.fetchPackageDependencies(
projectDir,
owner = project,
processListener = listener
).unwrapOrElse {
listener.error("Failed to fetch dependencies", it.message.orEmpty())
}
}
APTOS -> {
// TODO: not supported by CLI yet
}
}
}

private fun createSyncProgressDescriptor(progress: ProgressIndicator): BuildProgressDescriptor {
val buildContentDescriptor = BuildContentDescriptor(
Expand Down Expand Up @@ -295,20 +306,17 @@ private class SyncProcessAdapter(
private val context: MoveProjectsSyncTask.SyncContext
): ProcessAdapter(),
ProcessProgressListener {
// override fun onTextAvailable(event: ProcessEvent, outputType: Key<Any>) {
// val text = event.text.trim { it <= ' ' }
// if (text.startsWith("Updating") || text.startsWith("Downloading")) {
// context.withProgressText(text)
// }
// if (text.startsWith("Vendoring")) {
// // This code expect that vendoring message has the following format:
// // "Vendoring %package_name% v%package_version% (%src_dir%) to %dst_dir%".
// // So let's extract "Vendoring %package_name% v%package_version%" part and show it for users
// val index = text.indexOf(" (")
// val progressText = if (index != -1) text.substring(0, index) else text
// context.withProgressText(progressText)
// }
// }
override fun onTextAvailable(event: ProcessEvent, outputType: Key<Any>) {
val text = event.text.trim { it <= ' ' }
if (text.startsWith("FETCHING GIT DEPENDENCY")) {
val git = text.substring("FETCHING GIT DEPENDENCY ".length)
context.withProgressText("Fetching $git")
}
if (text.startsWith("UPDATING GIT DEPENDENCY")) {
val gitRepo = text.substring("UPDATING GIT DEPENDENCY ".length)
context.withProgressText("Updating $gitRepo")
}
}

override fun error(title: String, message: String) = context.error(title, message)
override fun warning(title: String, message: String) = context.warning(title, message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.intellij.openapi.project.Project
import org.move.cli.MoveProjectsService
import org.move.cli.MoveProjectsService.MoveRefreshStatus
import org.move.cli.moveProjectsService
import org.move.openapiext.saveAllDocuments

class MoveExternalSystemProjectAware(
private val project: Project
Expand All @@ -25,7 +26,8 @@ class MoveExternalSystemProjectAware(

override fun reloadProject(context: ExternalSystemProjectReloadContext) {
FileDocumentManager.getInstance().saveAllDocuments()
project.moveProjectsService.scheduleProjectsRefresh()
project.moveProjectsService.scheduleProjectsRefresh(
"from project aware, explicit=${context.isExplicitReload}")
}

override fun subscribe(listener: ExternalSystemProjectListener, parentDisposable: Disposable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import com.intellij.openapi.project.Project
import org.move.cli.Consts
import org.move.cli.MoveProject
import org.move.cli.moveProjectsService
import org.move.stdext.blankToNull

@Service(Service.Level.PROJECT)
class MoveSettingsFilesService(private val project: Project) {
Expand All @@ -23,11 +22,6 @@ class MoveSettingsFilesService(private val project: Project) {
for (movePackage in this.movePackages()) {
val root = movePackage.contentRoot.path
out.add("$root/${Consts.MANIFEST_FILE}")

val packageName = movePackage.packageName.blankToNull()
if (packageName != null) {
out.add("$root/build/$packageName/BuildInfo.yaml")
}
}
}

Expand Down
Loading
Loading