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..dcaa4fed804 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,10 @@ object MetalsEnrichments def isScalaProject(): Boolean = containsProjectFilesSatisfying(_.isScala) def isMetalsProject(): Boolean = - containsProjectFilesSatisfying(_.isScalaOrJavaFilename) + path.resolve(".metals").exists || + path.resolve(".bloop").exists || + path.resolve(".bsp").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()