From 97a4f7fc55a1f445e83fddfb57f35f8458202738 Mon Sep 17 00:00:00 2001 From: kasiaMarek Date: Fri, 17 Jan 2025 18:16:32 +0100 Subject: [PATCH 1/2] improvement: convert workspace folder to be a Metals project on chosen commands (e.g. import build) also assume, that workspace folder is Metals project if it contains `.bloop` folder --- .../internal/metals/MetalsEnrichments.scala | 4 +- .../internal/metals/WorkspaceFolders.scala | 2 +- .../internal/metals/WorkspaceLspService.scala | 38 ++++++++++++++++--- .../scala/tests/gradle/GradleLspSuite.scala | 32 ++++++++++++++++ 4 files changed, 68 insertions(+), 8 deletions(-) diff --git a/metals/src/main/scala/scala/meta/internal/metals/MetalsEnrichments.scala b/metals/src/main/scala/scala/meta/internal/metals/MetalsEnrichments.scala index 5f67f36eceb..03c80060476 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/MetalsEnrichments.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/MetalsEnrichments.scala @@ -393,7 +393,9 @@ object MetalsEnrichments def isScalaProject(): Boolean = containsProjectFilesSatisfying(_.isScala) def isMetalsProject(): Boolean = - containsProjectFilesSatisfying(_.isScalaOrJavaFilename) + path.resolve(".metals").exists || + path.resolve(".bloop").exists || + containsProjectFilesSatisfying(_.isScalaOrJavaFilename) private def containsProjectFilesSatisfying( fileNamePredicate: String => Boolean diff --git a/metals/src/main/scala/scala/meta/internal/metals/WorkspaceFolders.scala b/metals/src/main/scala/scala/meta/internal/metals/WorkspaceFolders.scala index 63192a77aea..27c83455251 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/WorkspaceFolders.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/WorkspaceFolders.scala @@ -102,7 +102,7 @@ class WorkspaceFolders( } yield () } - def convertToScalaProject(folder: Folder): Option[MetalsLspService] = { + def convertToScalaProject(folder: Folder): Option[ProjectMetalsLspService] = { val WorkspaceFoldersServices(after, _) = folderServices.updateAndGet { case wfs @ WorkspaceFoldersServices( diff --git a/metals/src/main/scala/scala/meta/internal/metals/WorkspaceLspService.scala b/metals/src/main/scala/scala/meta/internal/metals/WorkspaceLspService.scala index 42ed614af75..e1eeb0eede1 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/WorkspaceLspService.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/WorkspaceLspService.scala @@ -329,17 +329,40 @@ class WorkspaceLspService( def onCurrentFolder[A]( f: ProjectMetalsLspService => Future[A], actionName: String, + forceMetalsProject: Boolean, default: () => A, ): Future[A] = { + def forcedFocusedToBeMetalsProject(): Option[ProjectMetalsLspService] = + nonScalaProjects match { + case Nil => None + case head :: Nil if folderServices.isEmpty => + workspaceFolders.convertToScalaProject(head) + case _ => + focusedDocument + .get() + .flatMap(getFolderForOpt(_, nonScalaProjects)) + .flatMap(workspaceFolders.convertToScalaProject) + } def currentService(): Future[Option[ProjectMetalsLspService]] = folderServices match { + case Nil if forceMetalsProject => + Future { forcedFocusedToBeMetalsProject() } case Nil => Future { None } - case head :: Nil => Future { Some(head) } - case _ => + case head :: rest => focusedDocument.get().flatMap(getServiceForOpt) match { case Some(service) => Future { Some(service) } case None => - workspaceChoicePopup.interactiveChooseFolder(actionName) + if (rest.isEmpty) { + Future { Some(head) } + } else if (forceMetalsProject) { + forcedFocusedToBeMetalsProject() + .map(folder => Future.successful(Some(folder))) + .getOrElse( + workspaceChoicePopup.interactiveChooseFolder(actionName) + ) + } else { + workspaceChoicePopup.interactiveChooseFolder(actionName) + } } } currentService().flatMap { @@ -357,8 +380,9 @@ class WorkspaceLspService( def onCurrentFolder( f: ProjectMetalsLspService => Future[Unit], actionName: String, + forceMetalsProject: Boolean = false, ): Future[Unit] = - onCurrentFolder(f, actionName, () => ()) + onCurrentFolder(f, actionName, forceMetalsProject, () => ()) def foreachSeq[A]( f: ProjectMetalsLspService => Future[A], @@ -773,18 +797,21 @@ class WorkspaceLspService( onCurrentFolder( _.generateBspConfig(), ServerCommands.GenerateBspConfig.title, + forceMetalsProject = true, ).asJavaObject case ServerCommands.ImportBuild() => onCurrentFolder( _.connectionProvider.slowConnectToBuildServer(forceImport = true), ServerCommands.ImportBuild.title, default = () => BuildChange.None, + forceMetalsProject = true, ).asJavaObject case ServerCommands.ConnectBuildServer() => onCurrentFolder( _.connectionProvider.quickConnectToBuildServer(), ServerCommands.ConnectBuildServer.title, default = () => BuildChange.None, + forceMetalsProject = true, ).asJavaObject case ServerCommands.DisconnectBuildServer() => onCurrentFolder( @@ -1379,8 +1406,7 @@ class Folder( ) { lazy val isMetalsProject: Boolean = - isKnownMetalsProject || path.resolve(".metals").exists || path - .isMetalsProject() + isKnownMetalsProject || path.isMetalsProject() /** * A workspace folder might be a project reference for an other project. diff --git a/tests/slow/src/test/scala/tests/gradle/GradleLspSuite.scala b/tests/slow/src/test/scala/tests/gradle/GradleLspSuite.scala index 299f8918b47..389284796ba 100644 --- a/tests/slow/src/test/scala/tests/gradle/GradleLspSuite.scala +++ b/tests/slow/src/test/scala/tests/gradle/GradleLspSuite.scala @@ -67,6 +67,38 @@ class GradleLspSuite extends BaseImportSuite("gradle-import") { } } + test("i7031") { + cleanWorkspace() + for { + _ <- initialize( + Map( + "root" -> s"""|/build.gradle + |plugins { + | id 'scala' + |} + |repositories { + | mavenCentral() + |} + |dependencies { + | implementation 'org.scala-lang:scala-library:${V.scala213}' + |} + |""".stripMargin + ), + expectError = false, + ) + _ <- server.fullServer + .executeCommand(ServerCommands.ImportBuild.toExecuteCommandParams()) + .asScala + } yield { + assert(server.fullServer.folderServices.nonEmpty) + assert( + server.headServer.tables.buildTool + .selectedBuildTool() + .exists(_ == GradleBuildTool.name) + ) + } + } + test("inner") { client.importBuild = ImportBuild.yes cleanWorkspace() From 2592a64167b0dcfa828d6ccb368beb413a589113 Mon Sep 17 00:00:00 2001 From: kasiaMarek Date: Tue, 21 Jan 2025 17:51:10 +0100 Subject: [PATCH 2/2] add `.bsp` --- .../scala/scala/meta/internal/metals/MetalsEnrichments.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/metals/src/main/scala/scala/meta/internal/metals/MetalsEnrichments.scala b/metals/src/main/scala/scala/meta/internal/metals/MetalsEnrichments.scala index 03c80060476..dcaa4fed804 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/MetalsEnrichments.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/MetalsEnrichments.scala @@ -395,6 +395,7 @@ object MetalsEnrichments def isMetalsProject(): Boolean = path.resolve(".metals").exists || path.resolve(".bloop").exists || + path.resolve(".bsp").exists || containsProjectFilesSatisfying(_.isScalaOrJavaFilename) private def containsProjectFilesSatisfying(