From a6e1ac4c4e93cb3cf53e56928973aea3b4bcc6a5 Mon Sep 17 00:00:00 2001 From: Katarzyna Marek Date: Mon, 20 Nov 2023 17:25:49 +0100 Subject: [PATCH] look for `.bsp` also in the project root --- .../meta/internal/bsp/BspConnector.scala | 22 +++++++++++++- .../scala/meta/internal/bsp/BspServers.scala | 11 +++---- .../scala/meta/internal/builds/BspOnly.scala | 5 ++-- .../meta/internal/builds/BuildTools.scala | 30 +++++++++---------- .../scala/meta/internal/metals/Configs.scala | 2 +- .../internal/metals/UserConfiguration.scala | 12 ++++++++ .../src/main/scala/tests/TestingClient.scala | 2 -- .../src/test/scala/tests/BillLspSuite.scala | 16 +++++----- 8 files changed, 65 insertions(+), 35 deletions(-) diff --git a/metals/src/main/scala/scala/meta/internal/bsp/BspConnector.scala b/metals/src/main/scala/scala/meta/internal/bsp/BspConnector.scala index b0b29ee14ef..ce3ec891312 100644 --- a/metals/src/main/scala/scala/meta/internal/bsp/BspConnector.scala +++ b/metals/src/main/scala/scala/meta/internal/bsp/BspConnector.scala @@ -10,6 +10,7 @@ import scala.meta.internal.builds.BuildServerProvider import scala.meta.internal.builds.BuildTool import scala.meta.internal.builds.BuildTools import scala.meta.internal.builds.SbtBuildTool +import scala.meta.internal.builds.ScalaCliBuildTool import scala.meta.internal.builds.ShellRunner import scala.meta.internal.metals.BloopServers import scala.meta.internal.metals.BuildServerConnection @@ -19,6 +20,7 @@ import scala.meta.internal.metals.MetalsEnrichments._ import scala.meta.internal.metals.StatusBar import scala.meta.internal.metals.Tables import scala.meta.internal.metals.UserConfiguration +import scala.meta.internal.metals.scalacli.ScalaCli import scala.meta.internal.semver.SemVer import scala.meta.io.AbsolutePath @@ -58,7 +60,10 @@ class BspConnector( else bspServers .findAvailableServers() - .find(_.getName == sel) + .find(buildServer => + (ScalaCli.names(buildServer.getName()) && ScalaCli.names(sel)) || + buildServer.getName == sel + ) .map(ResolvedBspOne) } } @@ -129,6 +134,7 @@ class BspConnector( .map(Some(_)) case ResolvedBspOne(details) => tables.buildServers.chooseServer(details.getName()) + optSetBuildTool(details.getName()) bspServers .newServer(projectRoot, bspTraceRoot, details, bspStatusOpt) .map(Some(_)) @@ -163,6 +169,7 @@ class BspConnector( ) ) _ = tables.buildServers.chooseServer(item.getName()) + _ = optSetBuildTool(item.getName()) conn <- bspServers.newServer( projectRoot, bspTraceRoot, @@ -197,6 +204,19 @@ class BspConnector( } } + private def optSetBuildTool(buildServerName: String): Unit = + buildTools.loadSupported + .find { + case _: ScalaCliBuildTool if ScalaCli.names(buildServerName) => true + case buildTool: BuildServerProvider + if buildTool.buildServerName.contains(buildServerName) => + true + case buildTool => buildTool.executableName == buildServerName + } + .foreach(buildTool => + tables.buildTool.chooseBuildTool(buildTool.executableName) + ) + private def sbtMetaWorkspaces(root: AbsolutePath): List[AbsolutePath] = { def recursive( p: AbsolutePath, diff --git a/metals/src/main/scala/scala/meta/internal/bsp/BspServers.scala b/metals/src/main/scala/scala/meta/internal/bsp/BspServers.scala index f252c6abafb..838a6c185f8 100644 --- a/metals/src/main/scala/scala/meta/internal/bsp/BspServers.scala +++ b/metals/src/main/scala/scala/meta/internal/bsp/BspServers.scala @@ -47,6 +47,8 @@ final class BspServers( config: MetalsServerConfig, userConfig: () => UserConfiguration, )(implicit ec: ExecutionContextExecutorService) { + private def customProjectRoot = + userConfig().getCustomProjectRoot(mainWorkspace) def resolve(): BspResolvedResult = { findAvailableServers() match { @@ -153,7 +155,7 @@ final class BspServers( * may be a server in the current workspace */ def findAvailableServers(): List[BspConnectionDetails] = { - val jsonFiles = findJsonFiles(mainWorkspace) + val jsonFiles = findJsonFiles() val gson = new Gson() for { candidate <- jsonFiles @@ -172,9 +174,7 @@ final class BspServers( } } - private def findJsonFiles( - projectDirectory: AbsolutePath - ): List[AbsolutePath] = { + private def findJsonFiles(): List[AbsolutePath] = { val buf = List.newBuilder[AbsolutePath] def visit(dir: AbsolutePath): Unit = dir.list.foreach { p => @@ -182,7 +182,8 @@ final class BspServers( buf += p } } - visit(projectDirectory.resolve(".bsp")) + visit(mainWorkspace.resolve(".bsp")) + customProjectRoot.map(_.resolve(".bsp")).foreach(visit) bspGlobalInstallDirectories.foreach(visit) buf.result() } diff --git a/metals/src/main/scala/scala/meta/internal/builds/BspOnly.scala b/metals/src/main/scala/scala/meta/internal/builds/BspOnly.scala index fe6ef611789..7347a536600 100644 --- a/metals/src/main/scala/scala/meta/internal/builds/BspOnly.scala +++ b/metals/src/main/scala/scala/meta/internal/builds/BspOnly.scala @@ -7,16 +7,17 @@ import scala.meta.internal.mtags.MD5 import scala.meta.io.AbsolutePath /** - * Build tool for custom bsp detected in `.bsp/.json` + * Build tool for custom bsp detected in `.bsp/.json` or `bspGlobalDirectories` */ case class BspOnly( override val executableName: String, override val projectRoot: AbsolutePath, + pathToBspConfig: AbsolutePath, ) extends BuildTool { override def digest(workspace: AbsolutePath): Option[String] = { val digest = MessageDigest.getInstance("MD5") val isSuccess = - Digest.digestJson(workspace.resolve(s"$executableName.json"), digest) + Digest.digestJson(pathToBspConfig, digest) if (isSuccess) Some(MD5.bytesToHex(digest.digest())) else None } diff --git a/metals/src/main/scala/scala/meta/internal/builds/BuildTools.scala b/metals/src/main/scala/scala/meta/internal/builds/BuildTools.scala index 9d7c72a7cb0..bc36c2fdc34 100644 --- a/metals/src/main/scala/scala/meta/internal/builds/BuildTools.scala +++ b/metals/src/main/scala/scala/meta/internal/builds/BuildTools.scala @@ -55,6 +55,7 @@ final class BuildTools( def isBloop: Boolean = bloopProject.isDefined def isBsp: Boolean = { hasJsonFile(workspace.resolve(".bsp")) || + customProjectRoot.exists(root => hasJsonFile(root.resolve(".bsp"))) || bspGlobalDirectories.exists(hasJsonFile) } private def hasJsonFile(dir: AbsolutePath): Boolean = { @@ -123,34 +124,33 @@ final class BuildTools( def isBazel: Boolean = bazelProject.isDefined private def customBsps: List[BspOnly] = { - val bspFolder = workspace.resolve(".bsp") + val bspFolders = + (workspace :: customProjectRoot.toList).distinct + .map(_.resolve(".bsp")) ++ bspGlobalDirectories val root = customProjectRoot.getOrElse(workspace) - if (bspFolder.exists && bspFolder.isDirectory) { - bspFolder.toFile + for { + bspFolder <- bspFolders + if (bspFolder.exists && bspFolder.isDirectory) + buildTool <- bspFolder.toFile .listFiles() .collect { case file if file.isFile() && file.getName().endsWith(".json") && !knownBsps(file.getName().stripSuffix(".json")) => - BspOnly(file.getName().stripSuffix(".json"), root) + BspOnly( + file.getName().stripSuffix(".json"), + root, + AbsolutePath(file.toPath()), + ) } .toList - } else Nil + } yield buildTool } private def knownBsps = Set(SbtBuildTool.name, MillBuildTool.name) ++ ScalaCli.names - private def customProjectRoot = - userConfig().customProjectRoot - .map(relativePath => workspace.resolve(relativePath.trim())) - .filter { projectRoot => - val exists = projectRoot.exists - if (!exists) { - scribe.error(s"custom project root $projectRoot does not exist") - } - exists - } + private def customProjectRoot = userConfig().getCustomProjectRoot(workspace) private def searchForBuildTool( isProjectRoot: AbsolutePath => Boolean diff --git a/metals/src/main/scala/scala/meta/internal/metals/Configs.scala b/metals/src/main/scala/scala/meta/internal/metals/Configs.scala index ece48f7a172..874e9cacc33 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/Configs.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/Configs.scala @@ -40,7 +40,7 @@ object Configs { new FileSystemWatcher( Either.forLeft(s"$root/.metals/.reports/bloop/*/*") ), - new FileSystemWatcher(Either.forLeft(s"$root/.bsp/*.json")), + new FileSystemWatcher(Either.forLeft(s"$root/**/.bsp/*.json")), ).asJava ) } diff --git a/metals/src/main/scala/scala/meta/internal/metals/UserConfiguration.scala b/metals/src/main/scala/scala/meta/internal/metals/UserConfiguration.scala index 4361ec15ea4..c7ce292b43d 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/UserConfiguration.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/UserConfiguration.scala @@ -68,6 +68,18 @@ case class UserConfiguration( ) && this.showInferredType.nonEmpty showImplicitArguments || showInferredType || showImplicitConversionsAndClasses } + + def getCustomProjectRoot(workspace: AbsolutePath): Option[AbsolutePath] = + customProjectRoot + .map(relativePath => workspace.resolve(relativePath.trim())) + .filter { projectRoot => + val exists = projectRoot.toFile.exists + if (!exists) { + scribe.error(s"custom project root $projectRoot does not exist") + } + exists + } + } object UserConfiguration { diff --git a/tests/unit/src/main/scala/tests/TestingClient.scala b/tests/unit/src/main/scala/tests/TestingClient.scala index 9792c7821cf..687be471531 100644 --- a/tests/unit/src/main/scala/tests/TestingClient.scala +++ b/tests/unit/src/main/scala/tests/TestingClient.scala @@ -13,8 +13,6 @@ import scala.concurrent.Promise import scala.meta.inputs.Input import scala.meta.internal.bsp.ConnectionBspStatus -import scala.meta.internal.builds.BuildTool -import scala.meta.internal.builds.BspErrorHandler import scala.meta.internal.builds.BuildTools import scala.meta.internal.decorations.PublishDecorationsParams import scala.meta.internal.metals.Buffers diff --git a/tests/unit/src/test/scala/tests/BillLspSuite.scala b/tests/unit/src/test/scala/tests/BillLspSuite.scala index 35c26fe2b87..7c906d39578 100644 --- a/tests/unit/src/test/scala/tests/BillLspSuite.scala +++ b/tests/unit/src/test/scala/tests/BillLspSuite.scala @@ -187,9 +187,7 @@ class BillLspSuite extends BaseLspSuite("bill") { testRoundtripCompilation() } - def testSelectServerDialogue( - additionalMessages: List[String] = Nil - ): Future[Unit] = { + def testSelectServerDialogue(): Future[Unit] = { // when asked, choose the Bob build tool client.selectBspServer = { actions => actions.find(_.getTitle == "Bob").get @@ -203,11 +201,11 @@ class BillLspSuite extends BaseLspSuite("bill") { ) _ = assertNoDiff( client.workspaceMessageRequests, - (additionalMessages ++ - List( - Messages.BspSwitch.message, - Messages.CheckDoctor.allProjectsMisconfigured, - )).mkString("\n"), + List( + Messages.ChooseBuildTool.message, + Messages.BspSwitch.message, + Messages.CheckDoctor.allProjectsMisconfigured, + ).mkString("\n"), ) } yield () } @@ -216,7 +214,7 @@ class BillLspSuite extends BaseLspSuite("bill") { cleanWorkspace() Bill.installWorkspace(workspace.toNIO, "Bill") Bill.installWorkspace(workspace.toNIO, "Bob") - testSelectServerDialogue(List(Messages.ChooseBuildTool.message)) + testSelectServerDialogue() } test("mix") {