From 29d5b98bf2b10ca1438f602dc6dbbed272655a16 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Tue, 12 Nov 2024 16:52:59 +0100 Subject: [PATCH 1/7] add external formatter integration --- .../externalFormatter/MovefmtConfigurable.kt | 78 ++++++++++++ .../MovefmtFormattingService.kt | 114 ++++++++++++++++++ .../MovefmtSettingsService.kt | 55 +++++++++ src/main/kotlin/org/move/cli/tools/Movefmt.kt | 50 ++++++++ src/main/kotlin/org/move/lang/MoveFile.kt | 1 + src/main/kotlin/org/move/openapiext/utils.kt | 9 ++ src/main/resources/META-INF/plugin.xml | 6 + 7 files changed, 313 insertions(+) create mode 100644 src/main/kotlin/org/move/cli/externalFormatter/MovefmtConfigurable.kt create mode 100644 src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt create mode 100644 src/main/kotlin/org/move/cli/externalFormatter/MovefmtSettingsService.kt create mode 100644 src/main/kotlin/org/move/cli/tools/Movefmt.kt diff --git a/src/main/kotlin/org/move/cli/externalFormatter/MovefmtConfigurable.kt b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtConfigurable.kt new file mode 100644 index 000000000..40880b343 --- /dev/null +++ b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtConfigurable.kt @@ -0,0 +1,78 @@ +package org.move.cli.externalFormatter + +import com.intellij.execution.configuration.EnvironmentVariablesComponent +import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory +import com.intellij.openapi.options.BoundConfigurable +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.DialogPanel +import com.intellij.openapi.util.Disposer +import com.intellij.ui.RawCommandLineEditor +import com.intellij.ui.dsl.builder.AlignX +import com.intellij.ui.dsl.builder.bindSelected +import com.intellij.ui.dsl.builder.panel +import com.intellij.ui.dsl.builder.toMutableProperty +import org.move.openapiext.pathField + +class MovefmtConfigurable(val project: Project): BoundConfigurable("Movefmt") { + private val innerDisposable = Disposer.newCheckedDisposable() + + private val movefmtPathField = + pathField( + FileChooserDescriptorFactory.createSingleFileOrExecutableAppDescriptor(), + innerDisposable, + "Movefmt location") + private val additionalArguments: RawCommandLineEditor = RawCommandLineEditor() + private val environmentVariables: EnvironmentVariablesComponent = EnvironmentVariablesComponent() + + + override fun createPanel(): DialogPanel { + this.disposable?.let { + Disposer.register(it, innerDisposable) + } + return panel { + val settings = project.movefmtSettings + val state = settings.state.copy() + + row("Movefmt:") { + cell(movefmtPathField) + .align(AlignX.FILL).resizableColumn() + .bind( + componentGet = { it.text }, + componentSet = { component, value -> component.text = value }, + prop = state::movefmtPath.toMutableProperty() + ) + } + row("Additional arguments:") { + cell(additionalArguments) + .align(AlignX.FILL) + .comment("Additional arguments to pass to movefmt command") + .bind( + componentGet = { it.text }, + componentSet = { component, value -> component.text = value }, + prop = state::additionalArguments.toMutableProperty() + ) + } + row(environmentVariables.label) { + cell(environmentVariables).align(AlignX.FILL) + .bind( + componentGet = { it.envs }, + componentSet = { component, value -> component.envs = value }, + prop = state::envs.toMutableProperty() + ) + } + + row { checkBox("Use movefmt instead of the built-in formatter").bindSelected(state::useMovefmt) } +// row { checkBox("Run movefmt on Save").bindSelected(state::runRustfmtOnSave) } + + onApply { + settings.modify { + it.movefmtPath = state.movefmtPath + it.additionalArguments = state.additionalArguments + it.envs = state.envs + it.useMovefmt = state.useMovefmt +// it.runRustfmtOnSave = state.runRustfmtOnSave + } + } + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt new file mode 100644 index 000000000..a9f2b6fdf --- /dev/null +++ b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt @@ -0,0 +1,114 @@ +package org.move.cli.externalFormatter + +import com.intellij.codeInsight.actions.ReformatCodeProcessor +import com.intellij.execution.configuration.EnvironmentVariablesData +import com.intellij.execution.process.CapturingProcessAdapter +import com.intellij.execution.process.ProcessEvent +import com.intellij.formatting.service.AsyncDocumentFormattingService +import com.intellij.formatting.service.AsyncFormattingRequest +import com.intellij.formatting.service.FormattingService +import com.intellij.notification.NotificationType.INFORMATION +import com.intellij.openapi.command.CommandProcessor +import com.intellij.openapi.progress.util.ProgressIndicatorBase +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.io.toNioPathOrNull +import com.intellij.psi.PsiFile +import com.intellij.psi.formatter.FormatterUtil +import org.move.cli.externalFormatter.MovefmtFormattingService.Companion.FormattingReason.* +import org.move.cli.tools.Movefmt +import org.move.ide.notifications.showBalloon +import org.move.lang.MoveFile +import org.move.openapiext.rootPath +import org.move.stdext.blankToNull +import org.move.stdext.enumSetOf +import org.move.stdext.unwrapOrThrow + +class MovefmtFormattingService: AsyncDocumentFormattingService() { + // only whole file formatting is supported + override fun getFeatures(): Set = enumSetOf() + + override fun canFormat(file: PsiFile): Boolean = + file is MoveFile && file.project.movefmtSettings.useMovefmt && getFormattingReason() == ReformatCode + + override fun createFormattingTask(request: AsyncFormattingRequest): FormattingTask? { + val context = request.context + val project = context.project + val settings = project.movefmtSettings + + val disposable = Disposer.newDisposable() + val movefmtPath = settings.movefmtPath?.toNioPathOrNull() ?: return null + val movefmt = Movefmt(movefmtPath, disposable) + + val projectDirectory = project.rootPath ?: return null + val fileOnDisk = request.ioFile ?: return null + + return object: FormattingTask { + private val indicator: ProgressIndicatorBase = ProgressIndicatorBase() + + override fun run() { + val arguments = settings.additionalArguments.blankToNull()?.split(" ").orEmpty() + val envs = EnvironmentVariablesData.create(settings.envs, true) + val processOutput = movefmt.reformatFile( + fileOnDisk, + additionalArguments = arguments, + workingDirectory = projectDirectory, + envs, + runner = { + addProcessListener(object : CapturingProcessAdapter() { + override fun processTerminated(event: ProcessEvent) { + val exitCode = event.exitCode + if (exitCode == 0) { + val cleanedStdout = filterBuggyLines(output.stdout) + request.onTextReady(cleanedStdout) + } else { + request.onError("Movefmt", output.stderr) + } + } + }) + runProcessWithProgressIndicator(indicator) + } + ) + .unwrapOrThrow() + project.showBalloon("movefmt stdout", processOutput.stdout, INFORMATION) + } + + override fun cancel(): Boolean { + indicator.cancel() + disposable.dispose() + return true + } + + override fun isRunUnderProgress(): Boolean = true + } + } + + private fun filterBuggyLines(stdout: String): String { + return stdout.lines() + .dropWhile { !it.startsWith("options =") } + .filter { line -> + if (line.contains("options =")) return@filter false + if (line.contains("files successfully formatted")) return@filter false + true + } + .joinToString("\n").trimStart() + } + + override fun getNotificationGroupId(): String = "Move Language" + + override fun getName(): String = "movefmt" + + companion object { + private enum class FormattingReason { + ReformatCode, + ReformatCodeBeforeCommit, + Implicit + } + + private fun getFormattingReason(): FormattingReason = + when (CommandProcessor.getInstance().currentCommandName) { + ReformatCodeProcessor.getCommandName() -> ReformatCode + FormatterUtil.getReformatBeforeCommitCommandName() -> ReformatCodeBeforeCommit + else -> Implicit + } + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/cli/externalFormatter/MovefmtSettingsService.kt b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtSettingsService.kt new file mode 100644 index 000000000..d6da6ca4d --- /dev/null +++ b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtSettingsService.kt @@ -0,0 +1,55 @@ +package org.move.cli.externalFormatter + +import com.intellij.openapi.components.* +import com.intellij.openapi.components.Service.Level.PROJECT +import com.intellij.openapi.project.Project +import com.intellij.util.text.nullize +import org.move.cli.externalFormatter.MoveFmtSettingsService.MoveFmtSettings +import org.move.cli.settings.MvProjectSettingsServiceBase + +val Project.movefmtSettings: MoveFmtSettingsService + get() = service() + +private const val SERVICE_NAME: String = "org.move.MoveFmtSettingsService" + +@State( + name = SERVICE_NAME, + storages = [Storage(StoragePathMacros.WORKSPACE_FILE)], +) +@Service(PROJECT) +class MoveFmtSettingsService( + project: Project, +): MvProjectSettingsServiceBase(project, MoveFmtSettings()) { + + val useMovefmt: Boolean get() = state.useMovefmt +// val runMovefmtOnSave: Boolean get() = state.runMovefmtOnSave + + val movefmtPath: String? get() = state.movefmtPath.nullize() + val additionalArguments: String get() = state.additionalArguments + val envs: Map get() = state.envs + + class MoveFmtSettings: MvProjectSettingsBase() { + var useMovefmt by property(false) +// var runMovefmtOnSave by property(false) + + var movefmtPath by property("") { it.isEmpty() } + var additionalArguments by property("") { it.isEmpty() } + var envs by map() + + override fun copy(): MoveFmtSettings { + val state = MoveFmtSettings() + state.copyFrom(this) + return state + } + } + + override fun createSettingsChangedEvent( + oldEvent: MoveFmtSettings, + newEvent: MoveFmtSettings + ): SettingsChangedEvent = SettingsChangedEvent(oldEvent, newEvent) + + class SettingsChangedEvent( + oldState: MoveFmtSettings, + newState: MoveFmtSettings + ): SettingsChangedEventBase(oldState, newState) +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/cli/tools/Movefmt.kt b/src/main/kotlin/org/move/cli/tools/Movefmt.kt new file mode 100644 index 000000000..00a468993 --- /dev/null +++ b/src/main/kotlin/org/move/cli/tools/Movefmt.kt @@ -0,0 +1,50 @@ +package org.move.cli.tools + +import com.intellij.execution.configuration.EnvironmentVariablesData +import com.intellij.execution.process.CapturingProcessHandler +import com.intellij.execution.process.ProcessOutput +import com.intellij.openapi.Disposable +import com.intellij.openapi.util.Disposer +import com.intellij.util.containers.addAllIfNotNull +import org.move.openapiext.RsProcessResult +import org.move.openapiext.execute +import org.move.openapiext.runProcessWithGlobalProgress +import java.io.File +import java.nio.file.Path + + +class Movefmt(val cliLocation: Path, val parentDisposable: Disposable): Disposable.Default { + + // cannot make Movefmt CheckedDisposable, need to use another one + private val innerDisposable = Disposer.newCheckedDisposable("Movefmt disposable") + + init { + Disposer.register(this, innerDisposable) + Disposer.register(parentDisposable, this) + } + + fun reformatFile( + file: File, + additionalArguments: List, + workingDirectory: Path, + envs: EnvironmentVariablesData, + runner: CapturingProcessHandler.() -> ProcessOutput = { runProcessWithGlobalProgress() } + ): RsProcessResult { + val commandLine = MvCommandLine( + buildList { + add("-q") + addAllIfNotNull("--emit", "stdout") + if (additionalArguments.isNotEmpty()) { + addAll(additionalArguments) + } + add(file.absolutePath) + }, + workingDirectory = workingDirectory, + ) + return commandLine.toGeneralCommandLine(this.cliLocation) + .execute( + innerDisposable, + runner, + ) + } +} \ No newline at end of file diff --git a/src/main/kotlin/org/move/lang/MoveFile.kt b/src/main/kotlin/org/move/lang/MoveFile.kt index f39658bf3..a71067289 100644 --- a/src/main/kotlin/org/move/lang/MoveFile.kt +++ b/src/main/kotlin/org/move/lang/MoveFile.kt @@ -77,6 +77,7 @@ class MoveFile(fileViewProvider: FileViewProvider) : MoveFileBase(fileViewProvid fun moduleSpecs(): List = this.childrenOfType() } +val VirtualFile.isNotMoveFile: Boolean get() = !isMoveFile val VirtualFile.isMoveFile: Boolean get() = fileType == MoveFileType val VirtualFile.isMoveTomlManifestFile: Boolean get() = name == "Move.toml" diff --git a/src/main/kotlin/org/move/openapiext/utils.kt b/src/main/kotlin/org/move/openapiext/utils.kt index 85af5abf0..ed5b0add0 100644 --- a/src/main/kotlin/org/move/openapiext/utils.kt +++ b/src/main/kotlin/org/move/openapiext/utils.kt @@ -31,6 +31,7 @@ import com.intellij.openapi.roots.SyntheticLibrary import com.intellij.openapi.util.Computable import com.intellij.openapi.util.JDOMUtil import com.intellij.openapi.util.NlsContexts +import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.VfsUtil import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.* @@ -69,6 +70,12 @@ fun VirtualFile.toPsiFile(project: Project): PsiFile? = fun VirtualFile.toPsiDirectory(project: Project): PsiDirectory? = PsiManager.getInstance(project).findDirectory(this) +val Document.virtualFile: VirtualFile? + get() = FileDocumentManager.getInstance().getFile(this) + +val VirtualFile.document: Document? + get() = FileDocumentManager.getInstance().getDocument(this) + val PsiFile.document: Document? get() = PsiDocumentManager.getInstance(project).getDocument(this) @@ -245,3 +252,5 @@ inline fun testAssert(action: () -> Boolean, lazyMessage: () -> Any) { throw AssertionError(message) } } + +val String.escaped: String get() = StringUtil.escapeXmlEntities(this) \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 903947250..daa732cbb 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -105,6 +105,8 @@ + + @@ -340,6 +342,10 @@ parentId="language.move" id="language.move.compiler.check" displayName="External Linters" /> + Date: Sun, 17 Nov 2024 23:50:31 +0100 Subject: [PATCH 2/7] filter out lines correctly --- .../MovefmtFormattingService.kt | 20 ++++++------------- src/main/kotlin/org/move/cli/tools/Movefmt.kt | 6 +++++- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt index a9f2b6fdf..650845a71 100644 --- a/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt +++ b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt @@ -7,7 +7,6 @@ import com.intellij.execution.process.ProcessEvent import com.intellij.formatting.service.AsyncDocumentFormattingService import com.intellij.formatting.service.AsyncFormattingRequest import com.intellij.formatting.service.FormattingService -import com.intellij.notification.NotificationType.INFORMATION import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.progress.util.ProgressIndicatorBase import com.intellij.openapi.util.Disposer @@ -16,7 +15,6 @@ import com.intellij.psi.PsiFile import com.intellij.psi.formatter.FormatterUtil import org.move.cli.externalFormatter.MovefmtFormattingService.Companion.FormattingReason.* import org.move.cli.tools.Movefmt -import org.move.ide.notifications.showBalloon import org.move.lang.MoveFile import org.move.openapiext.rootPath import org.move.stdext.blankToNull @@ -48,18 +46,18 @@ class MovefmtFormattingService: AsyncDocumentFormattingService() { override fun run() { val arguments = settings.additionalArguments.blankToNull()?.split(" ").orEmpty() val envs = EnvironmentVariablesData.create(settings.envs, true) - val processOutput = movefmt.reformatFile( + movefmt.reformatFile( fileOnDisk, additionalArguments = arguments, workingDirectory = projectDirectory, envs, runner = { - addProcessListener(object : CapturingProcessAdapter() { + addProcessListener(object: CapturingProcessAdapter() { override fun processTerminated(event: ProcessEvent) { val exitCode = event.exitCode if (exitCode == 0) { - val cleanedStdout = filterBuggyLines(output.stdout) - request.onTextReady(cleanedStdout) + val filteredStdout = filterBuggyLines(output.stdout) + request.onTextReady(filteredStdout) } else { request.onError("Movefmt", output.stderr) } @@ -69,7 +67,6 @@ class MovefmtFormattingService: AsyncDocumentFormattingService() { } ) .unwrapOrThrow() - project.showBalloon("movefmt stdout", processOutput.stdout, INFORMATION) } override fun cancel(): Boolean { @@ -84,13 +81,8 @@ class MovefmtFormattingService: AsyncDocumentFormattingService() { private fun filterBuggyLines(stdout: String): String { return stdout.lines() - .dropWhile { !it.startsWith("options =") } - .filter { line -> - if (line.contains("options =")) return@filter false - if (line.contains("files successfully formatted")) return@filter false - true - } - .joinToString("\n").trimStart() + .takeWhile { !it.contains("files successfully formatted") } + .joinToString("\n") } override fun getNotificationGroupId(): String = "Move Language" diff --git a/src/main/kotlin/org/move/cli/tools/Movefmt.kt b/src/main/kotlin/org/move/cli/tools/Movefmt.kt index 00a468993..058abbad7 100644 --- a/src/main/kotlin/org/move/cli/tools/Movefmt.kt +++ b/src/main/kotlin/org/move/cli/tools/Movefmt.kt @@ -40,8 +40,12 @@ class Movefmt(val cliLocation: Path, val parentDisposable: Disposable): Disposab add(file.absolutePath) }, workingDirectory = workingDirectory, + environmentVariables = envs.with(mapOf("MOVEFMT_LOG" to "error")), ) - return commandLine.toGeneralCommandLine(this.cliLocation) + return commandLine + .toGeneralCommandLine(this.cliLocation) + // needs to skip stderr here + .withRedirectErrorStream(false) .execute( innerDisposable, runner, From dcc01d415573e82cc82ea96a454bd92a958c65d0 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Thu, 21 Nov 2024 13:55:47 +0100 Subject: [PATCH 3/7] get back to plain element instead of tree anchor in structure view --- .../MvStructureViewTreeElement.kt | 26 +++++++------------ 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/main/kotlin/org/move/ide/structureView/MvStructureViewTreeElement.kt b/src/main/kotlin/org/move/ide/structureView/MvStructureViewTreeElement.kt index 250ba5db4..dd990797f 100644 --- a/src/main/kotlin/org/move/ide/structureView/MvStructureViewTreeElement.kt +++ b/src/main/kotlin/org/move/ide/structureView/MvStructureViewTreeElement.kt @@ -1,8 +1,6 @@ package org.move.ide.structureView -import com.intellij.ide.projectView.PresentationData import com.intellij.ide.structureView.StructureViewTreeElement -import com.intellij.ide.util.treeView.TreeAnchorizer import com.intellij.ide.util.treeView.smartTree.TreeElement import com.intellij.navigation.ItemPresentation import com.intellij.openapi.ui.Queryable @@ -15,13 +13,13 @@ import org.move.lang.core.psi.* import org.move.lang.core.psi.ext.* import org.move.openapiext.common.isUnitTestMode -class MvStructureViewTreeElement(element: NavigatablePsiElement): StructureViewTreeElement, +class MvStructureViewTreeElement(val psi: NavigatablePsiElement): StructureViewTreeElement, Queryable { - val psiAnchor = TreeAnchorizer.getService().createAnchor(element) - val psi: NavigatablePsiElement? - get() = - TreeAnchorizer.getService().retrieveElement(psiAnchor) as? NavigatablePsiElement +// val psiAnchor = TreeAnchorizer.getService().createAnchor(element) +// val psi: NavigatablePsiElement? +// get() = +// TreeAnchorizer.getService().retrieveElement(psiAnchor) as? NavigatablePsiElement val isPublicItem: Boolean = when (val psi = psi) { @@ -34,16 +32,12 @@ class MvStructureViewTreeElement(element: NavigatablePsiElement): StructureViewT val isTestOnlyItem: Boolean get() = (psi as? MvDocAndAttributeOwner)?.hasTestOnlyAttr == true - override fun navigate(requestFocus: Boolean) { - psi?.navigate(requestFocus) - } - - override fun canNavigate(): Boolean = psi?.canNavigate() == true - override fun canNavigateToSource(): Boolean = psi?.canNavigateToSource() == true - override fun getValue(): PsiElement? = psi + override fun navigate(requestFocus: Boolean) = psi.navigate(requestFocus) + override fun canNavigate(): Boolean = psi.canNavigate() + override fun canNavigateToSource(): Boolean = psi.canNavigateToSource() + override fun getValue(): PsiElement = psi - override fun getPresentation(): ItemPresentation = - psi?.let(::getPresentationForStructure) ?: PresentationData("", null, null, null) + override fun getPresentation(): ItemPresentation = psi.let(::getPresentationForStructure) override fun getChildren(): Array = childElements.map2Array { MvStructureViewTreeElement(it) } From 4f9a58c8badaeb9918d8826186f0fe127a3219a9 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Thu, 21 Nov 2024 20:47:37 +0100 Subject: [PATCH 4/7] add notification for incorrectly configured movefmt --- .../MovefmtFormattingService.kt | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt index 650845a71..aa81eb124 100644 --- a/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt +++ b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt @@ -7,16 +7,23 @@ import com.intellij.execution.process.ProcessEvent import com.intellij.formatting.service.AsyncDocumentFormattingService import com.intellij.formatting.service.AsyncFormattingRequest import com.intellij.formatting.service.FormattingService +import com.intellij.notification.NotificationType.ERROR +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.command.CommandProcessor import com.intellij.openapi.progress.util.ProgressIndicatorBase +import com.intellij.openapi.project.DumbAwareAction import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.io.toNioPathOrNull import com.intellij.psi.PsiFile import com.intellij.psi.formatter.FormatterUtil import org.move.cli.externalFormatter.MovefmtFormattingService.Companion.FormattingReason.* import org.move.cli.tools.Movefmt +import org.move.ide.actions.EditMovefmtSettingsAction +import org.move.ide.notifications.showBalloon import org.move.lang.MoveFile import org.move.openapiext.rootPath +import org.move.openapiext.showSettingsDialog import org.move.stdext.blankToNull import org.move.stdext.enumSetOf import org.move.stdext.unwrapOrThrow @@ -34,9 +41,21 @@ class MovefmtFormattingService: AsyncDocumentFormattingService() { val settings = project.movefmtSettings val disposable = Disposer.newDisposable() - val movefmtPath = settings.movefmtPath?.toNioPathOrNull() ?: return null - val movefmt = Movefmt(movefmtPath, disposable) + val movefmtPath = settings.movefmtPath?.toNioPathOrNull() + if (movefmtPath == null) { + project.showBalloon(MOVEFMT_ERROR, + "movefmt executable configured incorrectly", + ERROR, + object : DumbAwareAction("Edit movefmt settings") { + override fun actionPerformed(e: AnActionEvent) { + e.project?.showSettingsDialog() + } + } + ) + return null + } + val movefmt = Movefmt(movefmtPath, disposable) val projectDirectory = project.rootPath ?: return null val fileOnDisk = request.ioFile ?: return null @@ -90,6 +109,8 @@ class MovefmtFormattingService: AsyncDocumentFormattingService() { override fun getName(): String = "movefmt" companion object { + private const val MOVEFMT_ERROR = "movefmt error" + private enum class FormattingReason { ReformatCode, ReformatCodeBeforeCommit, From fda1df3ecd7648df5e34716d01d3798548c5aa27 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Thu, 21 Nov 2024 20:55:37 +0100 Subject: [PATCH 5/7] fix imports --- .../org/move/cli/externalFormatter/MovefmtFormattingService.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt index aa81eb124..ac4e0d629 100644 --- a/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt +++ b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt @@ -19,7 +19,6 @@ import com.intellij.psi.PsiFile import com.intellij.psi.formatter.FormatterUtil import org.move.cli.externalFormatter.MovefmtFormattingService.Companion.FormattingReason.* import org.move.cli.tools.Movefmt -import org.move.ide.actions.EditMovefmtSettingsAction import org.move.ide.notifications.showBalloon import org.move.lang.MoveFile import org.move.openapiext.rootPath From 827d309294c4c938c9959511720b2ef307ccb188 Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Fri, 22 Nov 2024 14:50:26 +0100 Subject: [PATCH 6/7] add version label to movefmt executable --- .../externalFormatter/MovefmtConfigurable.kt | 19 +++++++++++++++++-- .../org/move/cli/settings/VersionLabel.kt | 14 +++++++++----- .../cli/settings/aptos/ChooseAptosCliPanel.kt | 4 ++-- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/org/move/cli/externalFormatter/MovefmtConfigurable.kt b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtConfigurable.kt index 40880b343..cd3b4c969 100644 --- a/src/main/kotlin/org/move/cli/externalFormatter/MovefmtConfigurable.kt +++ b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtConfigurable.kt @@ -1,26 +1,39 @@ package org.move.cli.externalFormatter import com.intellij.execution.configuration.EnvironmentVariablesComponent +import com.intellij.execution.configuration.EnvironmentVariablesData import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory import com.intellij.openapi.options.BoundConfigurable import com.intellij.openapi.project.Project import com.intellij.openapi.ui.DialogPanel import com.intellij.openapi.util.Disposer +import com.intellij.openapi.util.io.toNioPathOrNull import com.intellij.ui.RawCommandLineEditor import com.intellij.ui.dsl.builder.AlignX import com.intellij.ui.dsl.builder.bindSelected import com.intellij.ui.dsl.builder.panel import com.intellij.ui.dsl.builder.toMutableProperty +import org.move.cli.settings.VersionLabel import org.move.openapiext.pathField class MovefmtConfigurable(val project: Project): BoundConfigurable("Movefmt") { - private val innerDisposable = Disposer.newCheckedDisposable() + private val innerDisposable = + Disposer.newCheckedDisposable("Internal checked disposable for MovefmtConfigurable") private val movefmtPathField = pathField( FileChooserDescriptorFactory.createSingleFileOrExecutableAppDescriptor(), innerDisposable, - "Movefmt location") + "Movefmt location", + onTextChanged = { it -> + val path = it.toNioPathOrNull() ?: return@pathField + versionLabel.update(path) + }) + private val versionLabel = VersionLabel( + innerDisposable, + envs = EnvironmentVariablesData.create(mapOf("MOVEFMT_LOG" to "error"), true) + ) + private val additionalArguments: RawCommandLineEditor = RawCommandLineEditor() private val environmentVariables: EnvironmentVariablesComponent = EnvironmentVariablesComponent() @@ -42,6 +55,8 @@ class MovefmtConfigurable(val project: Project): BoundConfigurable("Movefmt") { prop = state::movefmtPath.toMutableProperty() ) } + row("--version :") { cell(versionLabel) } + separator() row("Additional arguments:") { cell(additionalArguments) .align(AlignX.FILL) diff --git a/src/main/kotlin/org/move/cli/settings/VersionLabel.kt b/src/main/kotlin/org/move/cli/settings/VersionLabel.kt index ec705dc18..4b3834d3c 100644 --- a/src/main/kotlin/org/move/cli/settings/VersionLabel.kt +++ b/src/main/kotlin/org/move/cli/settings/VersionLabel.kt @@ -1,10 +1,10 @@ package org.move.cli.settings -import com.intellij.openapi.Disposable +import com.intellij.execution.configuration.EnvironmentVariablesData import com.intellij.openapi.util.CheckedDisposable import com.intellij.ui.JBColor import com.intellij.ui.components.JBLabel -import org.move.cli.runConfigurations.AptosCommandLine +import org.move.cli.tools.MvCommandLine import org.move.openapiext.UiDebouncer import org.move.openapiext.checkIsBackgroundThread import org.move.openapiext.common.isUnitTestMode @@ -32,13 +32,14 @@ open class TextOrErrorLabel(icon: Icon?): JBLabel(icon) { class VersionLabel( parentDisposable: CheckedDisposable, + private val envs: EnvironmentVariablesData = EnvironmentVariablesData.DEFAULT, private val versionUpdateListener: (() -> Unit)? = null ): TextOrErrorLabel(null) { private val versionUpdateDebouncer = UiDebouncer(parentDisposable) - fun updateAndNotifyListeners(execPath: Path?) { + fun update(execPath: Path?) { versionUpdateDebouncer.update( onPooledThread = { if (!isUnitTestMode) { @@ -48,8 +49,11 @@ class VersionLabel( return@update null } - val commandLineArgs = - AptosCommandLine(null, listOf("--version"), workingDirectory = null) + val commandLineArgs = MvCommandLine( + listOf("--version"), + workingDirectory = null, + environmentVariables = envs + ) commandLineArgs .toGeneralCommandLine(execPath) .execute() diff --git a/src/main/kotlin/org/move/cli/settings/aptos/ChooseAptosCliPanel.kt b/src/main/kotlin/org/move/cli/settings/aptos/ChooseAptosCliPanel.kt index 6fe954ca7..eae31fe82 100644 --- a/src/main/kotlin/org/move/cli/settings/aptos/ChooseAptosCliPanel.kt +++ b/src/main/kotlin/org/move/cli/settings/aptos/ChooseAptosCliPanel.kt @@ -106,7 +106,7 @@ class ChooseAptosCliPanel(versionUpdateListener: (() -> Unit)?): Disposable { onTextChanged = { _ -> updateVersion() }) - private val versionLabel = VersionLabel(innerDisposable, versionUpdateListener) + private val versionLabel = VersionLabel(innerDisposable, versionUpdateListener = versionUpdateListener) private val bundledRadioButton = JBRadioButton("Bundled") private val localRadioButton = JBRadioButton("Local") @@ -194,7 +194,7 @@ class ChooseAptosCliPanel(versionUpdateListener: (() -> Unit)?): Disposable { private fun updateVersion() { val aptosPath = if (isBundledSelected) AptosExecType.bundledAptosCLIPath else localPathField.text.toNioPathOrNull() - versionLabel.updateAndNotifyListeners(aptosPath) + versionLabel.update(aptosPath) } fun updateAptosSdks(sdkPath: String) { From 94fc6cba2654e8ea14ff070552259990f01dc93f Mon Sep 17 00:00:00 2001 From: Maksim Kurnikov Date: Fri, 22 Nov 2024 15:09:14 +0100 Subject: [PATCH 7/7] error correctly on more cases --- .../MovefmtFormattingService.kt | 5 ++--- .../MovefmtSettingsService.kt | 21 +++++++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt index ac4e0d629..2bec04386 100644 --- a/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt +++ b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt @@ -40,8 +40,8 @@ class MovefmtFormattingService: AsyncDocumentFormattingService() { val settings = project.movefmtSettings val disposable = Disposer.newDisposable() - val movefmtPath = settings.movefmtPath?.toNioPathOrNull() - if (movefmtPath == null) { + val movefmt = project.getMovefmt(disposable) + if (movefmt == null) { project.showBalloon(MOVEFMT_ERROR, "movefmt executable configured incorrectly", ERROR, @@ -54,7 +54,6 @@ class MovefmtFormattingService: AsyncDocumentFormattingService() { return null } - val movefmt = Movefmt(movefmtPath, disposable) val projectDirectory = project.rootPath ?: return null val fileOnDisk = request.ioFile ?: return null diff --git a/src/main/kotlin/org/move/cli/externalFormatter/MovefmtSettingsService.kt b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtSettingsService.kt index d6da6ca4d..1f3ad7b6e 100644 --- a/src/main/kotlin/org/move/cli/externalFormatter/MovefmtSettingsService.kt +++ b/src/main/kotlin/org/move/cli/externalFormatter/MovefmtSettingsService.kt @@ -1,14 +1,18 @@ package org.move.cli.externalFormatter +import com.intellij.openapi.Disposable import com.intellij.openapi.components.* import com.intellij.openapi.components.Service.Level.PROJECT import com.intellij.openapi.project.Project +import com.intellij.openapi.util.io.toNioPathOrNull import com.intellij.util.text.nullize import org.move.cli.externalFormatter.MoveFmtSettingsService.MoveFmtSettings +import org.move.cli.runConfigurations.aptos.Aptos import org.move.cli.settings.MvProjectSettingsServiceBase - -val Project.movefmtSettings: MoveFmtSettingsService - get() = service() +import org.move.cli.settings.aptosCliPath +import org.move.cli.settings.isValidExecutable +import org.move.cli.tools.Movefmt +import org.move.openapiext.RootPluginDisposable private const val SERVICE_NAME: String = "org.move.MoveFmtSettingsService" @@ -52,4 +56,13 @@ class MoveFmtSettingsService( oldState: MoveFmtSettings, newState: MoveFmtSettings ): SettingsChangedEventBase(oldState, newState) -} \ No newline at end of file +} + +val Project.movefmtSettings: MoveFmtSettingsService get() = service() + +fun Project.getMovefmt(disposable: Disposable): Movefmt? { + val settings = this.movefmtSettings + val movefmtPath = settings.movefmtPath?.toNioPathOrNull()?.takeIf { it.isValidExecutable() } ?: return null + return Movefmt(movefmtPath, disposable) +} +