[Java] IDEA-385246 javac extra options completion in terminal

GitOrigin-RevId: 8e4e0e9993b29b0e1a55cb88cc19ba7e5ea63927
This commit is contained in:
Georgii Ustinov
2026-01-30 13:13:49 +00:00
committed by intellij-monorepo-bot
parent cb873a1dd7
commit 3b64d3885a
6 changed files with 116 additions and 22 deletions
@@ -92,3 +92,26 @@ vm.option.verbose.description=Outputs messages about what the compiler is doing,
vm.option.version.description=Prints version information.
vm.option.werror.description=Terminates compilation when warnings occur.
# javac -X
vm.option.default.module.for.created.files.description=Specifies the fallback target module for files created by annotation processors, if none is specified or inferred.
vm.option.disable.line.doc.comments.description=Disables support for documentation comments with lines beginning ///.
vm.option.help.lint.description=Prints the supported keys for the -Xlint option.
vm.option.patch.module.javac.description=Overrides or augments a module with classes and resources in JAR files or directories.
vm.option.bootclasspath.javac.description=Overrides the location of the bootstrap class files.
vm.option.bootclasspath.a.javac.description=Adds a suffix to the bootstrap class path.
vm.option.bootclasspath.p.javac.description=Adds a prefix to the bootstrap class path.
vm.option.diags.description=Selects a diagnostic mode.
vm.option.doclint.description=Enables recommended checks for problems in documentation comments.
vm.option.doclint.groups.description=Enables or disables specific groups of checks in documentation comments.
vm.option.doclint.package.description=Enables or disables checks in specific packages.
vm.option.xlint.description=Enables all recommended warnings.
vm.option.xlint.keys.description=Enables and/or disables specific warning categories.
vm.option.maxerrs.description=Sets the maximum number of errors to print.
vm.option.maxwarns.description=Sets the maximum number of warnings to print.
vm.option.pkginfo.description=Specifies when and how javac generates package-info.class files.
vm.option.plugin.description=Specifies the name and optional arguments for a plug-in to be run.
vm.option.prefer.description=Specifies which file to read when both a source file and class file are found for an implicitly compiled class.
vm.option.print.description=Prints out a textual representation of specified types for debugging purposes.
vm.option.print.processor.info.description=Prints information about which annotations a processor is asked to process.
vm.option.print.rounds.description=Prints information about annotation processing rounds.
vm.option.stdout.description=Redirects standard output to a file.
@@ -92,8 +92,8 @@ internal abstract class CommandOptionsExtractor {
private fun getOptionsForJava(javaHome: String): List<VMOption> {
val output = getProcessOutput(javaHome) ?: return STANDARD_OPTION_LIST
val xxOptions = VMOptionsParser.parseXXOptions(output.stdout)
val xOptions = VMOptionsParser.parseXOptions(output.stderr)
val xxOptions = VMOptionsParser.parseJavaXXOptions(output.stdout)
val xOptions = VMOptionsParser.parseJavaXOptions(output.stderr)
if (xOptions != null) {
return xOptions + xxOptions + STANDARD_OPTION_LIST
}
@@ -135,8 +135,11 @@ internal abstract class CommandOptionsExtractor {
private fun getOptionsForJavac(javaHome: String): List<VMOption> {
val processOutput = getProcessOutput(javaHome) ?: return emptyList()
val parsedStandardOptions = VMOptionsParser.parseJavacDoubleDashedOptions(processOutput.stdout)
return if (parsedStandardOptions != null) parsedStandardOptions + STANDARD_OPTIONS_LIST else STANDARD_OPTIONS_LIST
val result = mutableListOf<VMOption>()
result.addAll(STANDARD_OPTIONS_LIST)
VMOptionsParser.parseJavacDoubleDashedOptions(processOutput.stdout)?.let { result.addAll(it) }
VMOptionsParser.parseJavacXOptions(processOutput.stdout)?.let { result.addAll(it) }
return result
}
}
@@ -7,7 +7,7 @@ import org.jetbrains.annotations.PropertyKey
internal object VMOptionsParser {
private val LOG = Logger.getInstance(VMOptionsParser::class.java)
internal fun parseXXOptions(text: String) : List<VMOption> {
internal fun parseJavaXXOptions(text: String) : List<VMOption> {
val lines = text.lineSequence().drop(1)
val options = lines.mapNotNull {
val lbraceIndex = it.indexOf("{")
@@ -42,7 +42,7 @@ internal object VMOptionsParser {
return options
}
internal fun parseXOptions(stderr: String): List<VMOption>? {
internal fun parseJavaXOptions(stderr: String): List<VMOption>? {
var tailIndex = stderr.indexOf("These extra options are subject to change without notice.")
if (tailIndex == -1) {
tailIndex = stderr.indexOf("The -X options are non-standard and subject to change without notice.")
@@ -53,6 +53,17 @@ internal object VMOptionsParser {
return parseLines(ParsingType.JavaExtraOptions, stderr.substring(0, tailIndex).trimStart().lines(), mapOf("-X" to VMOptionVariant.X, "--" to VMOptionVariant.DASH_DASH), separators)
}
internal fun parseJavacXOptions(input: String): List<VMOption>? {
val lines = input.lines()
val beforeStartLine = lines.indexOf("")
if (beforeStartLine == -1) return null
val afterEndLine = lines.indexOf("These extra options are subject to change without notice.")
if (afterEndLine == -1) return null
if (beforeStartLine + 1 >= afterEndLine - 1) return null
return parseLines(ParsingType.JavacExtraOptions, lines.subList(beforeStartLine + 1, afterEndLine - 1), mapOf("-X" to VMOptionVariant.X, "--" to VMOptionVariant.DASH_DASH), charArrayOf(' '))
}
internal fun parseJavacDoubleDashedOptions(input: String): List<VMOption>? {
val lines = input.lines()
val lastLine = lines.indexOf("")
@@ -185,7 +196,24 @@ internal object VMOptionsParser {
}
}
object JavacStandardOptions : ParsingType {
interface JavacOptionsBase : ParsingType {
override fun parseName(input: String): String {
// parsing names "-opt:{arguments}"
input.extractPrefix(Regex(":[\"\\[{(]"), ":")?.let { return it }
// parsing names "-opt[=value]"
input.extractPrefix(Regex("\\["), "")?.let { return it }
return input.extractPrefix(Regex("<"), shouldHaveTwoParts = false) ?: throw IllegalArgumentException("Cannot parse option name: $input")
}
private fun String.extractPrefix(delimiter: Regex, suffix: String = "", shouldHaveTwoParts: Boolean = true): String? {
val parts = split(delimiter)
return if (parts.size == 2 || !shouldHaveTwoParts) parts.first().trimEnd(',') + suffix else null
}
}
object JavacStandardOptions : JavacOptionsBase {
override val optionDescriptionPropertyKey: Map<String, @PropertyKey(resourceBundle = "messages.VMOptionsBundle") String> = mapOf(
Pair("--add-modules", "vm.option.add.modules.description"),
Pair("--boot-class-path", "vm.option.boot.classpath.description"),
@@ -211,21 +239,37 @@ internal object VMOptionsParser {
)
override val kind: VMOptionKind = VMOptionKind.Standard
override fun parseName(input: String): String {
// parsing names "-opt:{arguments}"
input.extractPrefix(":{", ":")?.let { return it }
// parsing names "-opt[=value]"
input.extractPrefix("[", "")?.let { return it }
return input.extractPrefix("<", shouldHaveTwoParts = false) ?: throw IllegalArgumentException("Cannot parse option name: $input")
}
private fun String.extractPrefix(delimiter: String, suffix: String = "", shouldHaveTwoParts: Boolean = true): String? {
val parts = split(delimiter)
return if (parts.size == 2 || !shouldHaveTwoParts) parts.first().trimEnd(',') else null
}
}
object JavacExtraOptions : JavacOptionsBase {
override val optionDescriptionPropertyKey: Map<String, @PropertyKey(resourceBundle = "messages.VMOptionsBundle") String> = mapOf(
Pair("--add-exports", "vm.option.add.exports.description"),
Pair("--add-reads", "vm.option.add.reads.description"),
Pair("--default-module-for-created-files", "vm.option.default.module.for.created.files.description"),
Pair("--disable-line-doc-comments", "vm.option.disable.line.doc.comments.description"),
Pair("--help-lint", "vm.option.help.lint.description"),
Pair("--patch-module", "vm.option.patch.module.javac.description"),
Pair("-Xbootclasspath:", "vm.option.bootclasspath.javac.description"),
Pair("-Xbootclasspath/a:", "vm.option.bootclasspath.a.javac.description"),
Pair("-Xbootclasspath/p:", "vm.option.bootclasspath.p.javac.description"),
Pair("-Xdiags:", "vm.option.diags.description"),
Pair("-Xdoclint", "vm.option.doclint.description"),
Pair("-Xdoclint:", "vm.option.doclint.groups.description"),
Pair("-Xdoclint/package:", "vm.option.doclint.package.description"),
Pair("-Xlint", "vm.option.xlint.description"),
Pair("-Xlint:", "vm.option.xlint.keys.description"),
Pair("-Xmaxerrs", "vm.option.maxerrs.description"),
Pair("-Xmaxwarns", "vm.option.maxwarns.description"),
Pair("-Xpkginfo:", "vm.option.pkginfo.description"),
Pair("-Xplugin:", "vm.option.plugin.description"),
Pair("-Xprefer:", "vm.option.prefer.description"),
Pair("-Xprint", "vm.option.print.description"),
Pair("-XprintProcessorInfo", "vm.option.print.processor.info.description"),
Pair("-XprintRounds", "vm.option.print.rounds.description"),
Pair("-Xstdout", "vm.option.stdout.description"),
)
override val kind: VMOptionKind
get() = VMOptionKind.Product
}
}
}
@@ -105,4 +105,20 @@ private val UI_OPTION_INFO_MAP = mapOf(
"--system" to UIOptionInfo(argumentName = "jdk"),
"--target" to UIOptionInfo(argumentName = "release"),
"--upgrade-module-path" to UIOptionInfo(argumentName = "path", suggestionsGenerator = ShellDataGenerators.fileSuggestionsGenerator()),
"--add-exports" to UIOptionInfo(repeatTimes = 0, argumentName = "module/package=other-module(,other-module)*"),
"--add-reads" to UIOptionInfo(repeatTimes = 0, argumentName = "module=other-module(,other-module)*"),
"--default-module-for-created-files" to UIOptionInfo(argumentName = "module-name"),
"--patch-module" to UIOptionInfo(repeatTimes = 0, argumentName = "module=path"),
"-Xbootclasspath:" to UIOptionInfo(separator = "", argumentName = "path"),
"-Xbootclasspath/a:" to UIOptionInfo(separator = "", argumentName = "path"),
"-Xbootclasspath/p:" to UIOptionInfo(separator = "", argumentName = "path"),
"-Xdiags:" to UIOptionInfo(separator = "", isArgumentOptional = true, argumentName = "compact|verbose"),
"-Xdoclint:" to UIOptionInfo(separator = "", argumentName = "(none|all)"),
"-Xdoclint/package:" to UIOptionInfo(separator = "", argumentName = "(none|all)"),
"-Xlint:" to UIOptionInfo(separator = "", argumentName = "[-]key(,[-]key)*"),
"-Xmaxerrs" to UIOptionInfo(argumentName = "number"),
"-Xmaxwarns" to UIOptionInfo(argumentName = "number"),
"-Xpkginfo:" to UIOptionInfo(separator = "", argumentName = "always|legacy|nonempty", isArgumentOptional = true),
"-Xplugin:" to UIOptionInfo(separator = "", argumentName = "name"),
"-Xprefer:" to UIOptionInfo(separator = "", argumentName = "source|newer", isArgumentOptional = true),
)
@@ -44,6 +44,12 @@ internal class JavacShellCommandSpecsProviderTest(engine: TerminalEngine) : JdkC
UsefulTestCase.assertContainsElements(fixture.getCompletionNames("javac "), listOf("--enable-preview", "--release", "--source", "--target"))
}
@Test
fun `extra options are present`() = runBlocking {
val fixture = createFixture()
UsefulTestCase.assertContainsElements(fixture.getCompletionNames("javac "), listOf("-Xlint", "-Xbootclasspath:"))
}
@Test
fun `classpath suggestion generator`() = runBlocking {
val separator = JavaShellCommandUtils.getClassPathSeparator()
@@ -88,6 +88,8 @@ internal abstract class JdkCommandsShellSpecsProviderTestBase(private val engine
VMOption("source", null, null, VMOptionKind.Standard, null, VMOptionVariant.DASH_DASH),
VMOption("target", null, null, VMOptionKind.Standard, null, VMOptionVariant.DASH_DASH),
VMOption("experimental", null, null, VMOptionKind.Experimental, null, VMOptionVariant.DASH_DASH),
VMOption("lint", null, null, VMOptionKind.Product, null, VMOptionVariant.X),
VMOption("bootclasspath:", null, null, VMOptionKind.Product, null, VMOptionVariant.X),
)
)
)