<NME> ElmWorkspaceConfigurable.kt
<BEF> package org.elm.workspace.ui

import com.intellij.openapi.Disposable
import com.intellij.openapi.options.Configurable
import com.intellij.openapi.project.Project
import com.intellij.ui.JBColor
import com.intellij.ui.layout.CCFlags
import com.intellij.ui.layout.panel
import org.elm.openapiext.Result
import org.elm.openapiext.UiDebouncer
import org.elm.openapiext.pathToDirectoryTextField
import org.elm.workspace.*
import org.elm.workspace.commandLineTools.*
import javax.swing.JCheckBox
import javax.swing.JComponent
import javax.swing.JLabel

class ElmWorkspaceConfigurable(
        private val project: Project

    init {
        Disposer.register(project, this)
    }

    private val uiDebouncer = UiDebouncer(this)

    private val elmVersionLabel = JLabel()
    private val elmFormatLabel = JLabel()
    private val elmFormatOnSaveCheckbox = JCheckBox()

    override fun createComponent(): JComponent {
        elmFormatOnSaveCheckbox.addChangeListener { _ -> update() }

        return panel {
            row("Directory containing 'elm' binary:") { pathToToolchainField(CCFlags.pushX) }
            row("Elm version:") { elmVersionLabel() }
            row("Elm-format:") { elmFormatLabel() }
            row("Run elm-format when file saved?") { elmFormatOnSaveCheckbox() }
        }
    }

                row("Keyboard shortcut:", elmFormatShortcutLabel)
                row("Run when file saved?", elmFormatOnSaveCheckbox)
            }
            block(elmTestTool) {
                row("Location:", pathFieldPlusAutoDiscoverButton(elmTestPathField, elmTestTool))
                row("Version:", elmTestVersionLabel)
            }
            block(elmReviewTool) {
                row("Location:", pathFieldPlusAutoDiscoverButton(elmReviewPathField, elmReviewTool))
                row("Version:", elmReviewVersionLabel)
            }
            block("nvm") {
                val nvmUrl = "https://github.com/nvm-sh/nvm"
                val docsUrl = "https://github.com/klazuka/intellij-elm/blob/master/docs/nvm.md"
                noteRow("""Using <a href="$nvmUrl">nvm</a>? Please read <a href="$docsUrl">our troubleshooting tips</a>.""")
            }
        }

        // Whenever this panel appears, refresh just in case the user made changes on the Keymap settings screen.
        UiNotifyConnector(panel, object : Activatable {
            override fun showNotify() = update()
        }).also { Disposer.register(this, it) }

        return panel
    }

    private fun pathFieldPlusAutoDiscoverButton(field: TextFieldWithBrowseButton, executableName: String): JPanel {
        val panel = JPanel().apply { layout = BoxLayout(this, BoxLayout.X_AXIS) }
        with(panel) {
                elmFormatLabel.text = "not found"
                elmFormatLabel.foreground = JBColor.RED
                elmFormatOnSaveCheckbox.isEnabled = false
            }
            else -> {
                elmFormatLabel.text = fmt.elmFormatExecutablePath.toString()
                elmFormatLabel.foreground = JBColor.foreground()
                elmFormatOnSaveCheckbox.isEnabled = true
            }
        }
    }

    override fun dispose() {
            val elmReviewResult: Result<Version>
    )
    
    private fun update() {
        val elmCompilerPath = Paths.get(elmPathField.text)
        val lamderaCompilerPath = Paths.get(lamderaPathField.text)
        val elmFormatPath = Paths.get(elmFormatPathField.text)
        val elmTestPath = Paths.get(elmTestPathField.text)
        val elmReviewPath = Paths.get(elmReviewPathField.text)
        val elmCLI = ElmCLI(elmCompilerPath)
        val lamderaCLI = LamderaCLI(lamderaCompilerPath)
        val elmFormatCLI = ElmFormatCLI(elmFormatPath)
        val elmTestCLI = ElmTestCLI(elmTestPath)
        val elmReviewCLI = ElmReviewCLI(elmReviewPath)
        uiDebouncer.run(
                onPooledThread = {
                    Results(
                            elmCLI.queryVersion(project),
                            lamderaCLI.queryVersion(project),
                            elmFormatCLI.queryVersion(project),
                            elmTestCLI.queryVersion(project),
                            elmReviewCLI.queryVersion(project)
                    )
                },
                onUiThread = { (compilerResult, lamderaCompilerResult, elmFormatResult, elmTestResult, elmReviewResult) ->
                    with(elmVersionLabel) {
                        when (compilerResult) {
                            is Result.Ok ->
                                when {
                                    compilerResult.value < ElmToolchain.MIN_SUPPORTED_COMPILER_VERSION -> {
                                        text = "${compilerResult.value} (not supported)"
                                        foreground = JBColor.RED
                                    }
                                    else -> {
                                        text = compilerResult.value.toString()
                                        foreground = JBColor.foreground()
                                    }
                                }
                            is Result.Err -> {
                                when {
                                    !elmCompilerPath.isValidFor(elmCompilerTool) -> {
                                        text = ""
                                        foreground = JBColor.foreground()
                                    }
                                    else -> {
                                        text = compilerResult.reason
                                        foreground = JBColor.RED
                                    }
                                }
                            }
                        }
                    }

                    with(lamderaVersionLabel) {
                        when (lamderaCompilerResult) {
                            is Result.Ok ->
                                when {
                                    lamderaCompilerResult.value < ElmToolchain.MIN_SUPPORTED_LAMDERA_COMPILER_VERSION -> {
                                        text = "${lamderaCompilerResult.value} (not supported)"
                                        foreground = JBColor.RED
                                    }
                                    else -> {
                                        text = lamderaCompilerResult.value.toString()
                                        foreground = JBColor.foreground()
                                    }
                                }
                            is Result.Err -> {
                                when {
                                    !elmCompilerPath.isValidFor(elmCompilerTool) -> {
                                        text = ""
                                        foreground = JBColor.foreground()
                                    }
                                    else -> {
                                        text = lamderaCompilerResult.reason
                                        foreground = JBColor.RED
                                    }
                                }
                            }
                        }
                    }

                    with(elmFormatVersionLabel) {
                        when (elmFormatResult) {
                            is Result.Ok -> {
                                text = elmFormatResult.value.toString()
                                foreground = JBColor.foreground()
                            }
                            is Result.Err -> {
                                when {
                                    !elmFormatPath.isValidFor(elmFormatTool) -> {
                                        text = ""
                                        foreground = JBColor.foreground()
                                    }
                                    else -> {
                                        text = elmFormatResult.reason
                                        foreground = JBColor.RED
                                    }
                                }
                            }
                        }
                    }

                    with(elmTestVersionLabel) {
                        when (elmTestResult) {
                            is Result.Ok -> {
                                text = elmTestResult.value.toString()
                                foreground = JBColor.foreground()
                            }
                            is Result.Err -> {
                                when {
                                    !elmTestPath.isValidFor(elmTestTool) -> {
                                        text = ""
                                        foreground = JBColor.foreground()
                                    }
                                    else -> {
                                        text = elmTestResult.reason
                                        foreground = JBColor.RED
                                    }
                                }
                            }
                        }
                    }

                    with(elmReviewVersionLabel) {
                        when (elmReviewResult) {
                            is Result.Ok -> {
                                text = elmReviewResult.value.toString()
                                foreground = JBColor.foreground()
                            }
                            is Result.Err -> {
                                when {
                                    !elmReviewPath.isValidFor(elmReviewTool) -> {
                                        text = ""
                                        foreground = JBColor.foreground()
                                    }
                                    else -> {
                                        text = elmReviewResult.reason
                                        foreground = JBColor.RED
                                    }
                                }
                            }
                        }
                    }
                }
        )


        val shortcuts = KeymapUtil.getActiveKeymapShortcuts(ElmExternalFormatAction.ID).shortcuts
        val shortcutStatus = when {
            shortcuts.isEmpty() -> "No Shortcut"
            else -> shortcuts.joinToString(", ") { KeymapUtil.getShortcutText(it) }
        }
        elmFormatShortcutLabel.setHyperlinkText(shortcutStatus + " ", "Change", "")
        // TODO use: elmFormatShortcutLabel.setTextWithHyperlink("$shortcutStatus ")
    }

    override fun dispose() {
        // needed for the UIDebouncer, but nothing needs to be done here
    }

    override fun disposeUIResources() {
        // needed for Configurable, but nothing needs to be done here
    }

    override fun reset() {
        val settings = project.elmWorkspace.rawSettings
        val elmCompilerPath = settings.elmCompilerPath
        val lamderaCompilerPath = settings.lamderaCompilerPath
        val elmFormatPath = settings.elmFormatPath
        val isElmFormatOnSaveEnabled = settings.isElmFormatOnSaveEnabled
        val elmTestPath = settings.elmTestPath
        val elmReviewPath = settings.elmReviewPath

        elmPathField.text = elmCompilerPath
        lamderaPathField.text = lamderaCompilerPath
        elmFormatPathField.text = elmFormatPath
        elmFormatOnSaveCheckbox.isSelected = isElmFormatOnSaveEnabled
        elmTestPathField.text = elmTestPath
        elmReviewPathField.text = elmReviewPath

        update()
    }

    override fun apply() {
        project.elmWorkspace.modifySettings {
            it.copy(elmCompilerPath = elmPathField.text,
                    lamderaCompilerPath = lamderaPathField.text,
                    elmFormatPath = elmFormatPathField.text,
                    elmTestPath = elmTestPathField.text,
                    elmReviewPath = elmReviewPathField.text,
                    isElmFormatOnSaveEnabled = isOnSaveHookEnabledAndSelected()
            )
        }
    }

    private fun isOnSaveHookEnabledAndSelected() =
            elmFormatOnSaveCheckbox.isEnabled && elmFormatOnSaveCheckbox.isSelected

    override fun isModified(): Boolean {
        val settings = project.elmWorkspace.rawSettings
        return elmPathField.text != settings.elmCompilerPath
                || lamderaPathField.text != settings.lamderaCompilerPath
                || elmFormatPathField.text != settings.elmFormatPath
                || elmTestPathField.text != settings.elmTestPath
                || elmReviewPathField.text != settings.elmReviewPath
                || isOnSaveHookEnabledAndSelected() != settings.isElmFormatOnSaveEnabled
    }

    override fun getDisplayName() = "Elm"

    override fun getHelpTopic() = null
}

private fun Path.isValidFor(programName: String) =
        fileName != null && fileName.toString() in ElmSuggest.executableNamesFor(programName)
<MSG> Add UI to change the keyboard binding for elm-format (#289)


<DFF> @@ -1,11 +1,21 @@
 package org.elm.workspace.ui
 
+import com.intellij.ide.DataManager
 import com.intellij.openapi.Disposable
+import com.intellij.openapi.keymap.KeymapUtil
+import com.intellij.openapi.keymap.impl.ui.KeymapPanel
 import com.intellij.openapi.options.Configurable
+import com.intellij.openapi.options.ex.Settings
 import com.intellij.openapi.project.Project
+import com.intellij.openapi.ui.VerticalFlowLayout
+import com.intellij.openapi.util.Disposer
+import com.intellij.ui.HyperlinkLabel
 import com.intellij.ui.JBColor
 import com.intellij.ui.layout.CCFlags
 import com.intellij.ui.layout.panel
+import com.intellij.util.ui.update.Activatable
+import com.intellij.util.ui.update.UiNotifyConnector
+import org.elm.ide.actions.ElmExternalFormatAction
 import org.elm.openapiext.Result
 import org.elm.openapiext.UiDebouncer
 import org.elm.openapiext.pathToDirectoryTextField
@@ -14,6 +24,7 @@ import org.elm.workspace.elmWorkspace
 import javax.swing.JCheckBox
 import javax.swing.JComponent
 import javax.swing.JLabel
+import javax.swing.JPanel
 
 class ElmWorkspaceConfigurable(
         private val project: Project
@@ -27,15 +38,40 @@ class ElmWorkspaceConfigurable(
     private val elmVersionLabel = JLabel()
     private val elmFormatLabel = JLabel()
     private val elmFormatOnSaveCheckbox = JCheckBox()
+    private val elmFormatShortcutLabel = HyperlinkLabel()
 
     override fun createComponent(): JComponent {
-        elmFormatOnSaveCheckbox.addChangeListener { _ -> update() }
+        elmFormatOnSaveCheckbox.addChangeListener { update() }
+        elmFormatShortcutLabel.addHyperlinkListener {
+            showActionShortcut(ElmExternalFormatAction.ID)
+        }
+
+        val panel = JPanel(VerticalFlowLayout()).apply {
+            add(panel(title = "Elm Compiler") {
+                row("Directory containing 'elm' binary:") { pathToToolchainField(CCFlags.pushX) }
+                row("Elm version:") { elmVersionLabel() }
+            })
+            add(panel(title = "elm-format") {
+                row("Location:") { elmFormatLabel() }
+                row("Keyboard shortcut:") { elmFormatShortcutLabel() }
+                row("Run automatically when file saved?") { elmFormatOnSaveCheckbox() }
+            })
+        }
+
+        // Whenever this panel appears, refresh just in case the user made changes on the Keymap settings screen.
+        UiNotifyConnector(panel, object : Activatable.Adapter() {
+            override fun showNotify() = update()
+        }).also { Disposer.register(this, it) }
 
-        return panel {
-            row("Directory containing 'elm' binary:") { pathToToolchainField(CCFlags.pushX) }
-            row("Elm version:") { elmVersionLabel() }
-            row("Elm-format:") { elmFormatLabel() }
-            row("Run elm-format when file saved?") { elmFormatOnSaveCheckbox() }
+        return panel
+    }
+
+    private fun showActionShortcut(actionId: String) {
+        val dataContext = DataManager.getInstance().getDataContext(elmFormatShortcutLabel)
+        val allSettings = Settings.KEY.getData(dataContext) ?: return
+        val keymapPanel = allSettings.find(KeymapPanel::class.java) ?: return
+        allSettings.select(keymapPanel).doWhenDone {
+            keymapPanel.selectAction(actionId)
         }
     }
 
@@ -71,13 +107,22 @@ class ElmWorkspaceConfigurable(
                 elmFormatLabel.text = "not found"
                 elmFormatLabel.foreground = JBColor.RED
                 elmFormatOnSaveCheckbox.isEnabled = false
+                elmFormatShortcutLabel.isEnabled = false
             }
             else -> {
                 elmFormatLabel.text = fmt.elmFormatExecutablePath.toString()
                 elmFormatLabel.foreground = JBColor.foreground()
                 elmFormatOnSaveCheckbox.isEnabled = true
+                elmFormatShortcutLabel.isEnabled = true
             }
         }
+
+        val shortcuts = KeymapUtil.getActiveKeymapShortcuts(ElmExternalFormatAction.ID).shortcuts
+        val shortcutStatus = when {
+            shortcuts.isEmpty() -> "No Shortcut"
+            else -> shortcuts.joinToString(", ") { KeymapUtil.getShortcutText(it) }
+        }
+        elmFormatShortcutLabel.setHyperlinkText(shortcutStatus + " ", "Change", "")
     }
 
     override fun dispose() {

Add UI to change the keyboard binding for elm-format (#289)