diff --git a/main/src/main/scala/sbt/internal/CrossJava.scala b/main/src/main/scala/sbt/internal/CrossJava.scala index 845773b7e..813dd4c33 100644 --- a/main/src/main/scala/sbt/internal/CrossJava.scala +++ b/main/src/main/scala/sbt/internal/CrossJava.scala @@ -132,6 +132,9 @@ private[sbt] object CrossJava { private case class SwitchTarget(version: Option[JavaVersion], home: Option[File], force: Boolean) private case class SwitchJavaHome(target: SwitchTarget, verbose: Boolean, command: Option[String]) + private[sbt] val JavaSwitchCommandCompletions = + Vector("-v", "compile", "test", "run", "clean", "console", "package") + private def switchParser(state: State): Parser[SwitchJavaHome] = { import DefaultParsers.* def versionAndCommand(spacePresent: Boolean) = { @@ -141,9 +144,9 @@ private[sbt] object CrossJava { val knownVersions = javaHomes.keysIterator.map(_.numberStr).toVector val version: Parser[SwitchTarget] = (token( - (StringBasic <~ "@").? ~ ((NatBasic) ~ ("." ~> NatBasic).*) - .examples(knownVersions*) ~ "!".? + (StringBasic <~ "@").? ~ ((NatBasic) ~ ("." ~> NatBasic).*) ~ "!".? ) || token(StringBasic)) + .examples(knownVersions*) .map { case Left(((vendor, (v1, vs)), bang)) => val force = bang.isDefined @@ -156,7 +159,10 @@ private[sbt] object CrossJava { if (spacePresent) version else version & spacedFirst(JavaSwitchCommand) val verbose = Parser.opt(token(Space ~> "-v")) - val optionalCommand = Parser.opt(token(Space ~> matched(state.combinedParser))) + val optionalCommand = + Parser.opt( + token(Space ~> matched(state.combinedParser).examples(JavaSwitchCommandCompletions*)) + ) (spacedVersion ~ verbose ~ optionalCommand).map { case v ~ verbose ~ command => SwitchJavaHome(v, verbose.isDefined, command) } diff --git a/main/src/test/scala/sbt/internal/CrossJavaTest.scala b/main/src/test/scala/sbt/internal/CrossJavaTest.scala index bfd8c8059..8b10be51b 100644 --- a/main/src/test/scala/sbt/internal/CrossJavaTest.scala +++ b/main/src/test/scala/sbt/internal/CrossJavaTest.scala @@ -242,4 +242,15 @@ class CrossJavaTest extends AnyFunSuite with Diagrams { assert(version == "temurin@11.0.15") } } + + test("java++ tab completion list is bounded (#4310)"): + val completions = CrossJava.JavaSwitchCommandCompletions + assert(completions.nonEmpty, "completion list must not be empty") + assert( + completions.size <= 20, + "completion list must stay small to avoid JLine 'Display all N possibilities?'" + ) + assert(completions.contains("-v"), "completion list should suggest -v") + assert(completions.contains("compile"), "completion list should suggest compile") + assert(completions.contains("test"), "completion list should suggest test") }