-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #243 from pontem-network/integrate-with-new-formatter
add `movefmt` formatter integration
- Loading branch information
Showing
10 changed files
with
377 additions
and
23 deletions.
There are no files selected for viewing
93 changes: 93 additions & 0 deletions
93
src/main/kotlin/org/move/cli/externalFormatter/MovefmtConfigurable.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
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("Internal checked disposable for MovefmtConfigurable") | ||
|
||
private val movefmtPathField = | ||
pathField( | ||
FileChooserDescriptorFactory.createSingleFileOrExecutableAppDescriptor(), | ||
innerDisposable, | ||
"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() | ||
|
||
|
||
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("--version :") { cell(versionLabel) } | ||
separator() | ||
row("Additional arguments:") { | ||
cell(additionalArguments) | ||
.align(AlignX.FILL) | ||
.comment("Additional arguments to pass to <b>movefmt</b> 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 | ||
} | ||
} | ||
} | ||
} | ||
} |
125 changes: 125 additions & 0 deletions
125
src/main/kotlin/org/move/cli/externalFormatter/MovefmtFormattingService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
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.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.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 | ||
|
||
class MovefmtFormattingService: AsyncDocumentFormattingService() { | ||
// only whole file formatting is supported | ||
override fun getFeatures(): Set<FormattingService.Feature> = 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 movefmt = project.getMovefmt(disposable) | ||
if (movefmt == null) { | ||
project.showBalloon(MOVEFMT_ERROR, | ||
"movefmt executable configured incorrectly", | ||
ERROR, | ||
object : DumbAwareAction("Edit movefmt settings") { | ||
override fun actionPerformed(e: AnActionEvent) { | ||
e.project?.showSettingsDialog<MovefmtConfigurable>() | ||
} | ||
} | ||
) | ||
return null | ||
} | ||
|
||
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) | ||
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 filteredStdout = filterBuggyLines(output.stdout) | ||
request.onTextReady(filteredStdout) | ||
} else { | ||
request.onError("Movefmt", output.stderr) | ||
} | ||
} | ||
}) | ||
runProcessWithProgressIndicator(indicator) | ||
} | ||
) | ||
.unwrapOrThrow() | ||
} | ||
|
||
override fun cancel(): Boolean { | ||
indicator.cancel() | ||
disposable.dispose() | ||
return true | ||
} | ||
|
||
override fun isRunUnderProgress(): Boolean = true | ||
} | ||
} | ||
|
||
private fun filterBuggyLines(stdout: String): String { | ||
return stdout.lines() | ||
.takeWhile { !it.contains("files successfully formatted") } | ||
.joinToString("\n") | ||
} | ||
|
||
override fun getNotificationGroupId(): String = "Move Language" | ||
|
||
override fun getName(): String = "movefmt" | ||
|
||
companion object { | ||
private const val MOVEFMT_ERROR = "movefmt error" | ||
|
||
private enum class FormattingReason { | ||
ReformatCode, | ||
ReformatCodeBeforeCommit, | ||
Implicit | ||
} | ||
|
||
private fun getFormattingReason(): FormattingReason = | ||
when (CommandProcessor.getInstance().currentCommandName) { | ||
ReformatCodeProcessor.getCommandName() -> ReformatCode | ||
FormatterUtil.getReformatBeforeCommitCommandName() -> ReformatCodeBeforeCommit | ||
else -> Implicit | ||
} | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
src/main/kotlin/org/move/cli/externalFormatter/MovefmtSettingsService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
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 | ||
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" | ||
|
||
@State( | ||
name = SERVICE_NAME, | ||
storages = [Storage(StoragePathMacros.WORKSPACE_FILE)], | ||
) | ||
@Service(PROJECT) | ||
class MoveFmtSettingsService( | ||
project: Project, | ||
): MvProjectSettingsServiceBase<MoveFmtSettings>(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<String, String> get() = state.envs | ||
|
||
class MoveFmtSettings: MvProjectSettingsBase<MoveFmtSettings>() { | ||
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<String, String>() | ||
|
||
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<MoveFmtSettings>(oldState, newState) | ||
} | ||
|
||
val Project.movefmtSettings: MoveFmtSettingsService get() = service<MoveFmtSettingsService>() | ||
|
||
fun Project.getMovefmt(disposable: Disposable): Movefmt? { | ||
val settings = this.movefmtSettings | ||
val movefmtPath = settings.movefmtPath?.toNioPathOrNull()?.takeIf { it.isValidExecutable() } ?: return null | ||
return Movefmt(movefmtPath, disposable) | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.