diff --git a/bsp/src/org/jetbrains/bsp/BspUtil.scala b/bsp/src/org/jetbrains/bsp/BspUtil.scala index 8bd421fbe9e..219e99ca127 100644 --- a/bsp/src/org/jetbrains/bsp/BspUtil.scala +++ b/bsp/src/org/jetbrains/bsp/BspUtil.scala @@ -5,6 +5,7 @@ import java.net.URI import java.nio.file.{Path, Paths} import java.util.concurrent.CompletableFuture import com.intellij.build.events.impl.{FailureResultImpl, SkippedResultImpl, SuccessResultImpl} +import com.intellij.execution.configurations.GeneralCommandLine import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil import com.intellij.openapi.module.{Module, ModuleManager} import com.intellij.openapi.project.{Project, ProjectUtil} @@ -121,4 +122,20 @@ object BspUtil { .getOrElse(Array.empty) .find(x => x.getName == name && !x.isDirectory) + /** + * Checks whether a specified directory contains at least one file with a name from a given sequence of file names. + */ + def directoryContainsFile(directory: File, fileNames: String*): Boolean = + Option(directory.listFiles()) + .getOrElse(Array.empty) + .exists(x => !x.isDirectory && fileNames.contains(x.getName)) + + def checkIfToolIsInstalled(workspace: File, toolCommand: String): Boolean = + Try { + val generalCommandLine = new GeneralCommandLine(toolCommand, "version") + .withWorkDirectory(workspace) + val process = generalCommandLine.toProcessBuilder.start() + val exitValue = process.waitFor() + exitValue == 0 + }.getOrElse(false) } diff --git a/bsp/src/org/jetbrains/bsp/project/importing/MillProjectInstaller.scala b/bsp/src/org/jetbrains/bsp/project/importing/MillProjectInstaller.scala index bd9846c2425..02847d60715 100644 --- a/bsp/src/org/jetbrains/bsp/project/importing/MillProjectInstaller.scala +++ b/bsp/src/org/jetbrains/bsp/project/importing/MillProjectInstaller.scala @@ -16,7 +16,10 @@ class MillProjectInstaller extends BspProjectInstallProvider { override def canImport(workspace: File): Boolean = Option(workspace) match { - case Some(directory) if directory.isDirectory => isBspCompatible(directory) || isLegacyBspCompatible(directory) + case Some(directory) if directory.isDirectory => + BspUtil.directoryContainsFile(directory, "build.mill", "build.mill.scala") || + isBspCompatible(directory) || + isLegacyBspCompatible(directory) case _ => false } @@ -30,14 +33,24 @@ class MillProjectInstaller extends BspProjectInstallProvider { val isLegacyMill = !SystemInfo.isWindows && isLegacyBspCompatible(workspace) val millFileOpt = getMillFile(workspace) millFileOpt match { - case Some(file) if isMillFileBspCompatible(file, workspace) => - Success(Seq(file.getAbsolutePath, "-i", "mill.bsp.BSP/install")) - case Some(file) if isLegacyMill => + case Some(file) if isLegacyMill && !isMillFileBspCompatible(file, workspace) => + // run this only if we're confident this is legacy Mill Success(Seq(file.getAbsolutePath, "-i", "mill.contrib.BSP/install")) - case _ => Failure(new IllegalStateException("Unable to install BSP as this is not a Mill project")) + case Some(file) => + // otherwise run the normal BSP install command + Success(Seq(file.getAbsolutePath, "-i", "mill.bsp.BSP/install")) + //TODO: consider verifying Mill's installation in the #canImport to prevent its + // display in BspSetupConfigStepUi if not installed (the same in ScalaCliProjectInstaller) + case _ if isMillInstalled(workspace) => + // If the launcher is not found in the project root but Mill is available in the PATH, then we can use it. + Success(Seq("mill", "-i", "mill.bsp.BSP/install")) + case _ => Failure(new IllegalStateException("Installation of BSP is unable to proceed as the Mill executable is missing from both the project root and the PATH.")) } } + private def isMillInstalled(workspace: File): Boolean = + BspUtil.checkIfToolIsInstalled(workspace, "mill") + private def getMillFile(workspace: File): Option[File] = if (SystemInfo.isWindows) BspUtil.findFileByName(workspace, "mill.bat") else BspUtil.findFileByName(workspace, "mill") diff --git a/scala-cli/src/org/jetbrains/scalaCli/ScalaCliUtils.scala b/scala-cli/src/org/jetbrains/scalaCli/ScalaCliUtils.scala index 74599fdc17f..c063e780033 100644 --- a/scala-cli/src/org/jetbrains/scalaCli/ScalaCliUtils.scala +++ b/scala-cli/src/org/jetbrains/scalaCli/ScalaCliUtils.scala @@ -1,12 +1,11 @@ package org.jetbrains.scalaCli -import com.intellij.execution.configurations.GeneralCommandLine import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.project.Project +import org.jetbrains.bsp.BspUtil import org.jetbrains.bsp.project.BspExternalSystemUtil import java.io.File -import scala.util.Try object ScalaCliUtils { @@ -18,13 +17,7 @@ object ScalaCliUtils { } def isScalaCliInstalled(workspace: File): Boolean = - Try { - val generalCommandLine = new GeneralCommandLine(getScalaCliCommand, "version") - .withWorkDirectory(workspace) - val process = generalCommandLine.toProcessBuilder.start() - val exitValue = process.waitFor() - exitValue == 0 - }.getOrElse(false) + BspUtil.checkIfToolIsInstalled(workspace, getScalaCliCommand) /** * If these are tests, the Scala CLI is not installed globally - the script is only available in the project root directory,