From f2d0a7ace16de6a6e6abe5c76b3681be1cd56644 Mon Sep 17 00:00:00 2001 From: tgodzik Date: Sun, 3 Nov 2024 22:25:10 +0100 Subject: [PATCH] bugfix: Also discover synthetic mains such as scripts Fixes https://github.com/scalameta/metals/issues/6909 --- .../metals/codelenses/RunTestCodeLens.scala | 22 +++----- .../metals/debug/DebugDiscovery.scala | 34 ++++++++++-- .../tests/scalacli/ScalaCliDebugSuite.scala | 52 ++++++++++--------- 3 files changed, 64 insertions(+), 44 deletions(-) diff --git a/metals/src/main/scala/scala/meta/internal/metals/codelenses/RunTestCodeLens.scala b/metals/src/main/scala/scala/meta/internal/metals/codelenses/RunTestCodeLens.scala index 29bba0fec2b..84fe2d317d5 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/codelenses/RunTestCodeLens.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/codelenses/RunTestCodeLens.scala @@ -209,22 +209,14 @@ final class RunTestCodeLens( classes: BuildTargetClasses.Classes, isJVM: Boolean, ): Seq[l.CodeLens] = { - val symbolsWithMain = textDocument.symbols.filter(info => - classes.mainClasses.contains(info.symbol) - ) for { - sym <- symbolsWithMain - if ! { - textDocument.occurrences.exists(occ => - occ.symbol == sym.symbol && occ.role.isDefinition - ) - } - command <- classes.mainClasses - .get(sym.symbol) - .map( - mainCommand(target, _, isJVM, adjustName = s" (${sym.displayName})") - ) - .getOrElse(Nil) + main <- DebugDiscovery.syntheticMains(textDocument, classes) + command <- mainCommand( + target, + main, + isJVM, + adjustName = s" (${main.getClassName().split("\\.").last})", + ) } yield new l.CodeLens( new l.Range(new l.Position(0, 0), new l.Position(0, 0)), command, diff --git a/metals/src/main/scala/scala/meta/internal/metals/debug/DebugDiscovery.scala b/metals/src/main/scala/scala/meta/internal/metals/debug/DebugDiscovery.scala index 9da5efaf0ce..6f0876de93f 100644 --- a/metals/src/main/scala/scala/meta/internal/metals/debug/DebugDiscovery.scala +++ b/metals/src/main/scala/scala/meta/internal/metals/debug/DebugDiscovery.scala @@ -254,25 +254,30 @@ class DebugDiscovery( .fold[Future[b.DebugSessionParams]] { Future.failed(SemanticDbNotFoundException) } { textDocument => + val classes = buildTargetClasses.classesOf(buildTarget) lazy val tests = for { symbolInfo <- textDocument.symbols symbol = symbolInfo.symbol - testSymbolInfo <- testClasses(buildTarget).get(symbol) + testSymbolInfo <- classes.testClasses.get(symbol) } yield testSymbolInfo.fullyQualifiedName + val mains = for { occurrence <- textDocument.occurrences if occurrence.role.isDefinition || occurrence.symbol == "scala/main#" symbol = occurrence.symbol mainClass <- { - val normal = mainClasses(buildTarget).get(symbol) + val normal = classes.mainClasses.get(symbol) val fromAnnot = DebugDiscovery .mainFromAnnotation(occurrence, textDocument) - .flatMap(mainClasses(buildTarget).get(_)) + .flatMap(classes.mainClasses.get(_)) List(normal, fromAnnot).flatten } } yield mainClass - if (mains.nonEmpty) { - findMainToRun(Map(buildTarget -> mains.toList), params) + val mainWithFallback = + if (mains.nonEmpty) mains + else DebugDiscovery.syntheticMains(textDocument, classes) + if (mainWithFallback.nonEmpty) { + findMainToRun(Map(buildTarget -> mainWithFallback.toList), params) } else if (tests.nonEmpty) { DebugProvider.envFromFile(workspace, Option(params.envFile)).map { envFromFile => @@ -493,6 +498,25 @@ object DebugDiscovery { } + def syntheticMains( + textDocument: TextDocument, + classes: BuildTargetClasses.Classes, + ): Seq[b.ScalaMainClass] = { + val symbolsWithMain = textDocument.symbols.filter(info => + classes.mainClasses.contains(info.symbol) + ) + for { + sym <- symbolsWithMain + if ! { + textDocument.occurrences.exists(occ => + occ.symbol == sym.symbol && occ.role.isDefinition + ) + } + main <- classes.mainClasses + .get(sym.symbol) + } yield main + } + import scala.meta.internal.semanticdb.Scala._ /** diff --git a/tests/slow/src/test/scala/tests/scalacli/ScalaCliDebugSuite.scala b/tests/slow/src/test/scala/tests/scalacli/ScalaCliDebugSuite.scala index 47c7f88fd38..b43c00c2e0f 100644 --- a/tests/slow/src/test/scala/tests/scalacli/ScalaCliDebugSuite.scala +++ b/tests/slow/src/test/scala/tests/scalacli/ScalaCliDebugSuite.scala @@ -17,30 +17,34 @@ class ScalaCliDebugSuite ) { private val scalaCliScriptPath = "a/src/main/scala/a/main.sc" - test("run-scala-cli-script") { - for { - _ <- initialize( - s"""/.bsp/scala-cli.json - |${ScalaCli.scalaCliBspJsonContent()} - |/.scala-build/ide-inputs.json - |${BaseScalaCliSuite.scalaCliIdeInputJson(".")} - |/$scalaCliScriptPath - |print("oranges are nice")""".stripMargin - ) - _ <- server.didOpen(scalaCliScriptPath) - _ <- server.waitFor(TimeUnit.SECONDS.toMillis(10)) - debugger <- server.startDebuggingUnresolved( - DebugDiscoveryParams( - server.toPath(scalaCliScriptPath).toURI.toString, - "run", - ).toJson - ) - _ <- debugger.initialize - _ <- debugger.launch - _ <- debugger.configurationDone - _ <- debugger.shutdown - output <- debugger.allOutput - } yield assertNoDiff(output, "oranges are nice") + + for (runType <- List("runOrTestFile", "run")) { + + test(s"run-scala-cli-script-$runType") { + for { + _ <- initialize( + s"""/.bsp/scala-cli.json + |${ScalaCli.scalaCliBspJsonContent()} + |/.scala-build/ide-inputs.json + |${BaseScalaCliSuite.scalaCliIdeInputJson(".")} + |/$scalaCliScriptPath + |print("oranges are nice")""".stripMargin + ) + _ <- server.didOpen(scalaCliScriptPath) + _ <- server.waitFor(TimeUnit.SECONDS.toMillis(10)) + debugger <- server.startDebuggingUnresolved( + DebugDiscoveryParams( + server.toPath(scalaCliScriptPath).toURI.toString, + runType, + ).toJson + ) + _ <- debugger.initialize + _ <- debugger.launch + _ <- debugger.configurationDone + _ <- debugger.shutdown + output <- debugger.allOutput + } yield assertNoDiff(output, "oranges are nice") + } } }