From d6f1fc80e08c9c23899627ed9b806b2d8ac9a5f8 Mon Sep 17 00:00:00 2001 From: Konstantin Hudyakov Date: Mon, 15 Dec 2025 17:36:02 +0200 Subject: [PATCH] [terminal] IJPL-220311 Refactor ShellCommandParserOptions Make it an interface with a builder. GitOrigin-RevId: 2e9a6de571330fa8e44843c160fe5f5b4677bd19 --- .../terminal/JavaShellCommandSpecsProvider.kt | 5 +- .../engine/ShellCommandTokenNodes.kt | 6 +- .../spec/ShellCommandParserOptions.kt | 47 +++++++-------- .../spec/ShellCommandParserOptionsBuilder.kt | 60 +++++++++++++++++++ .../spec/json/ShellJsonBasedCommandSpec.kt | 10 ++-- .../ShellCommandSpecSuggestionsTest.kt | 4 +- .../completion/ShellCommandTreeBuilderTest.kt | 4 +- 7 files changed, 103 insertions(+), 33 deletions(-) create mode 100644 plugins/terminal/completion/src/com/intellij/terminal/completion/spec/ShellCommandParserOptionsBuilder.kt diff --git a/java/java-terminal/src/com/intellij/java/terminal/JavaShellCommandSpecsProvider.kt b/java/java-terminal/src/com/intellij/java/terminal/JavaShellCommandSpecsProvider.kt index a71b13c13d52..935cc75eb827 100644 --- a/java/java-terminal/src/com/intellij/java/terminal/JavaShellCommandSpecsProvider.kt +++ b/java/java-terminal/src/com/intellij/java/terminal/JavaShellCommandSpecsProvider.kt @@ -19,7 +19,10 @@ class JavaShellCommandSpecsProvider : ShellCommandSpecsProvider { } private fun getSpecs(): ShellCommandSpec = ShellCommandSpec("java") { - parserOptions = ShellCommandParserOptions.create(flagsArePosixNonCompliant = true, optionsMustPrecedeArguments = true) + parserOptions = ShellCommandParserOptions.builder() + .flagsArePosixNonCompliant(true) + .optionsMustPrecedeArguments(true) + .build() dynamicOptions { terminalContext -> val javaContext = JavaShellCommandContext.create(terminalContext) diff --git a/plugins/terminal/completion/src/com/intellij/terminal/completion/engine/ShellCommandTokenNodes.kt b/plugins/terminal/completion/src/com/intellij/terminal/completion/engine/ShellCommandTokenNodes.kt index 8f87c577c657..2929bbce9513 100644 --- a/plugins/terminal/completion/src/com/intellij/terminal/completion/engine/ShellCommandTokenNodes.kt +++ b/plugins/terminal/completion/src/com/intellij/terminal/completion/engine/ShellCommandTokenNodes.kt @@ -32,7 +32,11 @@ internal class ShellCommandNode( val flagsArePosixNonCompliant = if (child.flagsArePosixNonCompliant) true else base.flagsArePosixNonCompliant val optionsMustPrecedeArguments = if (child.optionsMustPrecedeArguments) true else base.optionsMustPrecedeArguments val optionArgSeparators = (base.optionArgSeparators + child.optionArgSeparators).distinct() - return ShellCommandParserOptions.create(flagsArePosixNonCompliant, optionsMustPrecedeArguments, optionArgSeparators) + return ShellCommandParserOptions.builder() + .flagsArePosixNonCompliant(flagsArePosixNonCompliant) + .optionsMustPrecedeArguments(optionsMustPrecedeArguments) + .optionArgSeparators(optionArgSeparators) + .build() } } diff --git a/plugins/terminal/completion/src/com/intellij/terminal/completion/spec/ShellCommandParserOptions.kt b/plugins/terminal/completion/src/com/intellij/terminal/completion/spec/ShellCommandParserOptions.kt index 937b168bb583..296823969a0e 100644 --- a/plugins/terminal/completion/src/com/intellij/terminal/completion/spec/ShellCommandParserOptions.kt +++ b/plugins/terminal/completion/src/com/intellij/terminal/completion/spec/ShellCommandParserOptions.kt @@ -4,32 +4,31 @@ package com.intellij.terminal.completion.spec import org.jetbrains.annotations.ApiStatus @ApiStatus.Experimental -class ShellCommandParserOptions private constructor( - val flagsArePosixNonCompliant: Boolean, - val optionsMustPrecedeArguments: Boolean, +sealed interface ShellCommandParserOptions { + /** + * @see [ShellCommandParserOptionsBuilder.flagsArePosixNonCompliant] + */ + val flagsArePosixNonCompliant: Boolean + + /** + * @see [ShellCommandParserOptionsBuilder.optionsMustPrecedeArguments] + */ + val optionsMustPrecedeArguments: Boolean + + /** + * @see [ShellCommandParserOptionsBuilder.optionArgSeparators] + */ val optionArgSeparators: List -) { - override fun toString(): String { - return "ShellCommandParserDirectives(flagsArePosixNonCompliant=$flagsArePosixNonCompliant, optionsMustPrecedeArguments=$optionsMustPrecedeArguments, optionArgSeparators=$optionArgSeparators)" - } companion object { - val DEFAULT = create() + val DEFAULT: ShellCommandParserOptions = builder().build() - /** - * @param flagsArePosixNonCompliant whether options starting with one hyphen ('-a', '-l', etc.) may have more than one character. - * If this option is true, then completion engine will parse `-abc` as a single shell option instead of chained options `-a`, `-b` and `-c`. - * False by default. - * @param optionsMustPrecedeArguments if true, the options won't be suggested after any argument of the command is typed. False by default. - * @param optionArgSeparators allows specifying that the option that takes the argument will require having one of these separators - * between the option name and the argument value. - */ - fun create( - flagsArePosixNonCompliant: Boolean = false, - optionsMustPrecedeArguments: Boolean = false, - optionArgSeparators: List = emptyList() - ): ShellCommandParserOptions { - return ShellCommandParserOptions(flagsArePosixNonCompliant, optionsMustPrecedeArguments, optionArgSeparators) - } + fun builder(): ShellCommandParserOptionsBuilder = ShellCommandParserOptionsBuilderImpl() } -} \ No newline at end of file +} + +internal data class ShellCommandParserOptionsImpl( + override val flagsArePosixNonCompliant: Boolean, + override val optionsMustPrecedeArguments: Boolean, + override val optionArgSeparators: List, +) : ShellCommandParserOptions \ No newline at end of file diff --git a/plugins/terminal/completion/src/com/intellij/terminal/completion/spec/ShellCommandParserOptionsBuilder.kt b/plugins/terminal/completion/src/com/intellij/terminal/completion/spec/ShellCommandParserOptionsBuilder.kt new file mode 100644 index 000000000000..6034f8e62353 --- /dev/null +++ b/plugins/terminal/completion/src/com/intellij/terminal/completion/spec/ShellCommandParserOptionsBuilder.kt @@ -0,0 +1,60 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.terminal.completion.spec + +import org.jetbrains.annotations.ApiStatus + +@ApiStatus.Experimental +sealed interface ShellCommandParserOptionsBuilder { + /** + * Whether options starting with one hyphen ('-a', '-l', etc.) may have more than one character. + * If this option is true, then completion engine will parse `-abc` as a single shell option + * instead of chained options `-a`, `-b` and `-c`. + * + * False by default. + */ + fun flagsArePosixNonCompliant(value: Boolean): ShellCommandParserOptionsBuilder + + /** + * If true, the options won't be suggested after any argument of the command is typed. + * + * False by default. + */ + fun optionsMustPrecedeArguments(value: Boolean): ShellCommandParserOptionsBuilder + + /** + * Allows specifying that the option that takes the argument will require having one of these separators + * between the option name and the argument value. + */ + fun optionArgSeparators(values: List): ShellCommandParserOptionsBuilder + + fun build(): ShellCommandParserOptions +} + +internal class ShellCommandParserOptionsBuilderImpl : ShellCommandParserOptionsBuilder { + private var flagsArePosixNonCompliant: Boolean = false + private var optionsMustPrecedeArguments: Boolean = false + private var optionArgSeparators: List = emptyList() + + override fun flagsArePosixNonCompliant(value: Boolean): ShellCommandParserOptionsBuilder { + flagsArePosixNonCompliant = value + return this + } + + override fun optionsMustPrecedeArguments(value: Boolean): ShellCommandParserOptionsBuilder { + optionsMustPrecedeArguments = value + return this + } + + override fun optionArgSeparators(values: List): ShellCommandParserOptionsBuilder { + optionArgSeparators = values + return this + } + + override fun build(): ShellCommandParserOptions { + return ShellCommandParserOptionsImpl( + flagsArePosixNonCompliant = flagsArePosixNonCompliant, + optionsMustPrecedeArguments = optionsMustPrecedeArguments, + optionArgSeparators = optionArgSeparators + ) + } +} \ No newline at end of file diff --git a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/completion/spec/json/ShellJsonBasedCommandSpec.kt b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/completion/spec/json/ShellJsonBasedCommandSpec.kt index a691a2a8698c..f113697f8349 100644 --- a/plugins/terminal/src/org/jetbrains/plugins/terminal/block/completion/spec/json/ShellJsonBasedCommandSpec.kt +++ b/plugins/terminal/src/org/jetbrains/plugins/terminal/block/completion/spec/json/ShellJsonBasedCommandSpec.kt @@ -38,11 +38,11 @@ internal class ShellJsonBasedCommandSpec( get() = data.requiresSubcommand override val parserOptions: ShellCommandParserOptions by lazy { - ShellCommandParserOptions.create( - data.parserDirectives.flagsArePosixNoncompliant, - data.parserDirectives.optionsMustPrecedeArguments, - data.parserDirectives.optionArgSeparators - ) + ShellCommandParserOptions.builder() + .flagsArePosixNonCompliant(data.parserDirectives.flagsArePosixNoncompliant) + .optionsMustPrecedeArguments(data.parserDirectives.optionsMustPrecedeArguments) + .optionArgSeparators(data.parserDirectives.optionArgSeparators) + .build() } val fullSpecRef: String? diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/block/completion/ShellCommandSpecSuggestionsTest.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/block/completion/ShellCommandSpecSuggestionsTest.kt index ca2683b337e1..c4a9d160ffe8 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/block/completion/ShellCommandSpecSuggestionsTest.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/block/completion/ShellCommandSpecSuggestionsTest.kt @@ -131,7 +131,9 @@ internal class ShellCommandSpecSuggestionsTest(private val engine: TerminalEngin } subcommand("optPrecedeArgs") { - parserOptions = ShellCommandParserOptions.create(optionsMustPrecedeArguments = true) + parserOptions = ShellCommandParserOptions.builder() + .optionsMustPrecedeArguments(true) + .build() option("-c") option("-d") argument { diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/block/completion/ShellCommandTreeBuilderTest.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/block/completion/ShellCommandTreeBuilderTest.kt index 86e464ffd421..3a133d070b6f 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/block/completion/ShellCommandTreeBuilderTest.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/block/completion/ShellCommandTreeBuilderTest.kt @@ -124,7 +124,9 @@ internal class ShellCommandTreeBuilderTest(private val engine: TerminalEngine) { } subcommand("nonPosix") { - parserOptions = ShellCommandParserOptions.create(flagsArePosixNonCompliant = true) + parserOptions = ShellCommandParserOptions.builder() + .flagsArePosixNonCompliant(true) + .build() option("-a") option("-b") }