<NME> StdlibInstallHelper.kt
<BEF> package org.elm.workspace

import com.intellij.openapi.project.Project
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.LocalFileSystem
import org.elm.fileTree
import org.elm.openapiext.pathAsPath
import org.elm.workspace.ElmToolchain.Companion.ELM_JSON
import org.intellij.lang.annotations.Language
import java.nio.file.Path
import java.nio.file.Paths

/*
Some of the Elm tests depend on having certain Elm packages installed in the global location.
And since Elm has pretty poor facilities for installing packages, we have to resort to tricks
like asking it to compile a dummy application.
 */

interface ElmStdlibVariant {

    /**
     * A description of the packages that we want to be installed
     */
    val jsonManifest: String

    /**
     * Install Elm 0.19 stdlib in the default location ($HOME/.elm)
     * @return An application Elm project which depends on the specified Stdlib packages
     */
    fun ensureElmStdlibInstalled(project: Project, toolchain: ElmToolchain): ElmProject {
        val compilerVersion = toolchain.queryCompilerVersion().orNull()
                ?: error("Could not query the Elm compiler version")
        require(compilerVersion != Version(0, 18, 0))

        val elm = toolchain.elmCompilerPath?.let { ElmCLI(it) }
                ?: error("Must have a path to the Elm compiler to install Elm stdlib")

        // Create the dummy Elm project on-disk (real file system) and invoke the Elm compiler on it.
        val onDiskTmpDir = LocalFileSystem.getInstance()
                .refreshAndFindFileByIoFile(FileUtil.createTempDirectory("elm-stdlib-variant", null, true))
        @Language("JSON")
        get() = """
            {
                "type": "application",
            file("Main.elm", elmHeadlessWorkerCode)
        }.create(project, onDiskTmpDir)

//        println("-----------------------------")
//        println("Installing Deps for $this")
        val output = elm.installDeps(project, onDiskTmpDir.pathAsPath.resolve("elm.json"))
//        println("STDOUT: ${output.stdout}")
//        println("\n")
//        println("STDERR: ${output.stderr}")
//        println("-----------------------------")

        // Now return an `ElmProject` with a manifest path suitable for IntelliJ's "light"
        // integration tests which put everything at `/src` using the in-memory VFS.
                },
                "test-dependencies": {
                    "direct": {},
                    "indirect": {}
                }
            }
            """.trimIndent()
}

/**
 * Describes a "minimal" installation of the Elm stdlib. This is the bare minimum
 * required to compile an Elm application.
 */
object MinimalElmStdlibVariant : ElmStdlibVariant {
    override val jsonManifest: String
        @Language("JSON")
        get() = """
            {
                "type": "application",
                "source-directories": [
                    "."
                ],
                "elm-version": "0.19.1",
                "dependencies": {
                    "direct": {
                        "elm/core": "1.0.0",
                        "elm/json": "1.0.0"
                    },
                    "indirect": {}
                },
                "test-dependencies": {
                    "direct": {},
                    "indirect": {}
                }
            }
            """.trimIndent()

    override fun ensureElmStdlibInstalled(project: Project, toolchain: ElmToolchain) {
        val elmCLI = toolchain.elmCLI
                ?: error("Must have a path to the Elm compiler to install Elm stdlib")

        val compilerVersion = elmCLI.queryVersion(project).orNull()
                ?: error("Could not query the Elm compiler version")
        require(compilerVersion != Version(0, 18, 0))

        // Create the dummy Elm project on-disk (real file system) and invoke the Elm compiler on it.
        val onDiskTmpDir = LocalFileSystem.getInstance()
                .refreshAndFindFileByIoFile(FileUtil.createTempDirectory("elm-stdlib-variant", null, true))
                ?: error("Could not create on-disk temp dir for Elm stdlib installation")

        fileTree {
            project(ELM_JSON, jsonManifest)
            elm("Main.elm")
        }.create(project, onDiskTmpDir)

        val entryPoint: Triple<Path, String?, Int> = Triple(
            Paths.get("Main.elm"),
            null,
            0 // mainEntryPoint.textOffset
        )
        elmCLI.make(project, onDiskTmpDir.pathAsPath, null, listOf(entryPoint))
    }
}

<MSG> Allow user to configure location of `elm-format` and `elm-test` executables (#311)

I also refactored `ElmToolchain` and related classes.

Notable Changes:
- the user can now set the path to `elm-format` and `elm-test`
- added an "Auto Discover" button to the settings UI to find each tool based on common locations
- the path to `elm-format` and `elm-test` are now persisted in workspace settings
- code related to suggesting program locations has been moved into its own class, `ElmSuggest`
- created an `ElmTestCLI` class to match the existing `ElmCLI` and `ElmFormatCLI` classes
- the toolchain on the ElmWorkspace is no longer nullable
<DFF> @@ -28,13 +28,13 @@ interface ElmStdlibVariant {
      * @return An application Elm project which depends on the specified Stdlib packages
      */
     fun ensureElmStdlibInstalled(project: Project, toolchain: ElmToolchain): ElmProject {
-        val compilerVersion = toolchain.queryCompilerVersion().orNull()
+        val elmCLI = toolchain.elmCLI
+                ?: error("Must have a path to the Elm compiler to install Elm stdlib")
+
+        val compilerVersion = elmCLI.queryVersion().orNull()
                 ?: error("Could not query the Elm compiler version")
         require(compilerVersion != Version(0, 18, 0))
 
-        val elm = toolchain.elmCompilerPath?.let { ElmCLI(it) }
-                ?: error("Must have a path to the Elm compiler to install Elm stdlib")
-
         // Create the dummy Elm project on-disk (real file system) and invoke the Elm compiler on it.
         val onDiskTmpDir = LocalFileSystem.getInstance()
                 .refreshAndFindFileByIoFile(FileUtil.createTempDirectory("elm-stdlib-variant", null, true))
@@ -45,13 +45,7 @@ interface ElmStdlibVariant {
             file("Main.elm", elmHeadlessWorkerCode)
         }.create(project, onDiskTmpDir)
 
-//        println("-----------------------------")
-//        println("Installing Deps for $this")
-        val output = elm.installDeps(project, onDiskTmpDir.pathAsPath.resolve("elm.json"))
-//        println("STDOUT: ${output.stdout}")
-//        println("\n")
-//        println("STDERR: ${output.stderr}")
-//        println("-----------------------------")
+        elmCLI.installDeps(project, onDiskTmpDir.pathAsPath.resolve("elm.json"))
 
         // Now return an `ElmProject` with a manifest path suitable for IntelliJ's "light"
         // integration tests which put everything at `/src` using the in-memory VFS.

Allow user to configure location of `elm-format` and `elm-test` executables (#311)