Skip to content

Commit

Permalink
Add mix format external format processor
Browse files Browse the repository at this point in the history
Fixes KronicDeth#997

To format manually:

With the keyboard

* Mac: ⌥⇧⌘L
* Linux/Windows: Ctrl+Alt+Shift+L

From the menus

1. Code
2. Reformat File
3. Click Run in the "Reformat File" dialog

To tun on format on save:

1. Preferences
2. Tools > Actions on Save.
3. Check "Reformat code".
4. Make sure "All file types" is set or at least "Files: Elixir" is set.

Autosave

JetBrains IDEs have autosave turned on by default, but you can adjust the settings:

1. Preferences
2. Appearance & Behavior > System Settings.
3. Check or uncheck the settings in the Autosave section.
  • Loading branch information
KronicDeth committed May 27, 2022
1 parent 78be640 commit e112efe
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 88 deletions.
88 changes: 0 additions & 88 deletions src/org/elixir_lang/formatter/MixFmtExternalFormatProcessor.java

This file was deleted.

110 changes: 110 additions & 0 deletions src/org/elixir_lang/formatter/MixFmtExternalFormatProcessor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package org.elixir_lang.formatter

import com.intellij.execution.process.CapturingProcessHandler
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiFile
import com.intellij.psi.codeStyle.ExternalFormatProcessor
import org.elixir_lang.Mix
import org.elixir_lang.psi.ElixirFile
import org.elixir_lang.sdk.elixir.Type.Companion.mostSpecificSdk
import java.util.concurrent.TimeUnit

@Suppress("UnstableApiUsage")
@SuppressWarnings("UnstableApiUsage")
class MixFmtExternalFormatProcessor : ExternalFormatProcessor {
override fun activeForFile(source: PsiFile): Boolean {
return source is ElixirFile
}

override fun getId(): String = "elixir_lang.mixFmtExternalFormatProcessor"

override fun format(
source: PsiFile,
range: TextRange,
canChangeWhiteSpacesOnly: Boolean,
keepLineBreaks: Boolean,
enableBulkUpdate: Boolean,
cursorOffset: Int
): TextRange? =
// mix fmt doesn't support to format parts of a file
if (source.isValid && range == source.textRange) {
source.viewProvider.document?.let { document ->
val application = ApplicationManager.getApplication()

application.executeOnPooledThread {
workingDirectory(source)?.let { workingDirectory ->
mostSpecificSdk(source)?.let { sdk ->
format(workingDirectory, sdk, document.text)?.let { formattedText ->
application.invokeLater {
if (source.isValid) {
CommandProcessor.getInstance().runUndoTransparentAction {
application.runWriteAction {
document.setText(formattedText)
}
}
}
}
}
}
}
}

source.textRange
}
} else {
null
}

override fun indent(source: PsiFile, lineStartOffset: Int): String? = null

companion object {
private val LOGGER = Logger.getInstance(MixFmtExternalFormatProcessor::class.java)

private fun workingDirectory(psiFile: PsiFile): String? =
psiFile
.virtualFile
?.let { virtualFile ->
ProjectRootManager.getInstance(psiFile.project).fileIndex.getContentRootForFile(virtualFile)
}
?.takeIf { contentRoot ->
contentRoot.findChild("mix.exs") != null
}
?.path

private fun format(workingDirectory: String, sdk: Sdk, unformattedText: String): String? {
val commandline = Mix.commandLine(emptyMap(), workingDirectory, sdk)
commandline.addParameter("format")
// `-` turns on stdin/stdout for text to format
commandline.addParameter("-")
val processHandler = CapturingProcessHandler(commandline)
processHandler.processInput.use { stdin ->
stdin.write(unformattedText.toByteArray())
stdin.flush()
}

val indicator = ProgressManager.getGlobalProgressIndicator()
val timeout = TimeUnit.SECONDS.toMillis(5).toInt()
val output = if (indicator != null) {
processHandler.runProcessWithProgressIndicator(
indicator,
timeout,
true
)
} else {
processHandler.runProcess(timeout, true)
}

return if (output.checkSuccess(LOGGER)) {
output.stdout
} else {
null
}
}
}
}

0 comments on commit e112efe

Please sign in to comment.