[terminal] IJPL-220311 Refactor completion specs DSL

Use methods instead of `var` properties:
1. It makes no sense for the client to allow reading the current state of property, the only purpose is to set the value.
2. Method way allows making boolean setters without parameters, and all other setters accept not null values. So, client should specify something only when they need to change the default.

GitOrigin-RevId: 7764edbc0165bec9fff999a96880b33c8ad4b6ad
This commit is contained in:
Konstantin Hudyakov
2025-12-19 12:42:20 +00:00
committed by intellij-monorepo-bot
parent 0496bb4f77
commit 06282c73d2
20 changed files with 259 additions and 191 deletions
@@ -19,10 +19,12 @@ class JavaShellCommandSpecsProvider : ShellCommandSpecsProvider {
}
private fun getSpecs(): ShellCommandSpec = ShellCommandSpec("java") {
parserOptions = ShellCommandParserOptions.builder()
.flagsArePosixNonCompliant(true)
.optionsMustPrecedeArguments(true)
.build()
parserOptions(
ShellCommandParserOptions.builder()
.flagsArePosixNonCompliant(true)
.optionsMustPrecedeArguments(true)
.build()
)
dynamicOptions { terminalContext ->
val javaContext = JavaShellCommandContext.create(terminalContext)
@@ -38,7 +40,7 @@ class JavaShellCommandSpecsProvider : ShellCommandSpecsProvider {
val errorStreamName: @Nls String = JavaTerminalBundle.message("error.stream.name")
description(JavaTerminalBundle.message("java.command.terminal.description"))
option("-?", "-help", "-h") {
exclusiveOn = listOf("--help")
exclusiveOn(listOf("--help"))
description(JavaTerminalBundle.message("java.command.terminal.help.option.description", errorStreamName))
}
option("-jar") {
@@ -49,11 +51,11 @@ class JavaShellCommandSpecsProvider : ShellCommandSpecsProvider {
description(JavaTerminalBundle.message("java.command.terminal.jar.option.description"))
}
option("-version") {
exclusiveOn = listOf("--version")
exclusiveOn(listOf("--version"))
description(JavaTerminalBundle.message("java.command.terminal.version.option.description", errorStreamName))
}
option("-classpath", "-cp") {
exclusiveOn = listOf("--class-path")
exclusiveOn(listOf("--class-path"))
description(JavaTerminalBundle.message("java.command.terminal.classpath.option.description"))
argument {
suggestions(JavaShellCommandUtils.classpathSuggestionsGenerator())
@@ -61,7 +63,7 @@ class JavaShellCommandSpecsProvider : ShellCommandSpecsProvider {
}
}
option("-showversion") {
exclusiveOn = listOf("--show-version")
exclusiveOn(listOf("--show-version"))
description(JavaTerminalBundle.message("java.command.terminal.show.version.option.description", errorStreamName))
}
argument {
@@ -88,12 +90,12 @@ class JavaShellCommandSpecsProvider : ShellCommandSpecsProvider {
description(optionDescription)
val info = OPTION_UI_INFO_MAP[presentableName] ?: DEFAULT_UI_OPTION_INSTANCE
repeatTimes = info.repeatTimes
separator = info.separator
repeatTimes(info.repeatTimes)
info.separator?.let { separator(it) }
val argumentName = info.argumentName
if (argumentName != null) {
argument {
isOptional = info.isArgumentOptional
if (info.isArgumentOptional) optional()
displayName(argumentName)
}
}
@@ -104,15 +106,15 @@ class JavaShellCommandSpecsProvider : ShellCommandSpecsProvider {
private fun ShellChildOptionsContext.addOptionsFromJava11() {
val outputStreamName = JavaTerminalBundle.message("output.stream.name")
option("--version") {
exclusiveOn = listOf("-version")
exclusiveOn(listOf("-version"))
description(JavaTerminalBundle.message("java.command.terminal.version.option.description", outputStreamName))
}
option("--show-version") {
exclusiveOn = listOf("-show-version")
exclusiveOn(listOf("-show-version"))
description(JavaTerminalBundle.message("java.command.terminal.show.version.option.description", outputStreamName))
}
option("--dry-run") {
exclusiveOn = listOf("-dry-run")
exclusiveOn(listOf("-dry-run"))
description(JavaTerminalBundle.message("java.command.terminal.dry.run.option.description"))
}
option("--class-path") {
@@ -123,7 +125,7 @@ class JavaShellCommandSpecsProvider : ShellCommandSpecsProvider {
}
}
option("--help") {
exclusiveOn = listOf("-?", "-help", "-h")
exclusiveOn(listOf("-?", "-help", "-h"))
description(JavaTerminalBundle.message("java.command.terminal.help.option.description", outputStreamName))
}
option("--enable-preview") {
@@ -132,10 +134,10 @@ class JavaShellCommandSpecsProvider : ShellCommandSpecsProvider {
option("-verbose") {
description(JavaTerminalBundle.message("java.command.terminal.verbose.option.description"))
separator=":"
repeatTimes = 0
separator(":")
repeatTimes(0)
argument {
isOptional = true
optional()
displayName(CLASS_GC_GNI_MODULE_ARGUMENT_NAME)
}
}
@@ -144,10 +146,10 @@ class JavaShellCommandSpecsProvider : ShellCommandSpecsProvider {
private fun ShellChildOptionsContext.addOptionsFromJava8() {
option("-verbose") {
description(JavaTerminalBundle.message("java.command.terminal.verbose.option.description"))
separator=":"
repeatTimes = 0
separator(":")
repeatTimes(0)
argument {
isOptional = true
optional()
displayName(CLASS_GC_GNI_ARGUMENT_NAME)
}
}
@@ -40,7 +40,7 @@ internal fun aliasGenerator(): ShellRuntimeDataGenerator<List<ShellAliasSuggesti
internal fun ShellCommandContext.addGitAliases() {
argument {
isOptional = true
optional()
suggestions(aliasGenerator())
}
}
@@ -165,7 +165,7 @@ private val localOrRemoteBranchesGenerator: ShellRuntimeDataGenerator<List<Shell
private fun branchSuggestion(name: String, description: @Nls String? = null, priority: Int = 50): ShellCompletionSuggestion {
return ShellCompletionSuggestion(name) {
if (description != null) description(description)
this.priority = priority
priority(priority)
icon(AllIcons.Vcs.Branch)
}
}
@@ -179,11 +179,9 @@ private fun ShellCommandContext.trackingOptions() {
}
argument {
displayName(GitTerminalBundle.message("option.track.arg2.name"))
isOptional = true
optional()
}
exclusiveOn = listOf(
"--no-track"
)
exclusiveOn(listOf("--no-track"))
}
option("--no-track") {
description(GitTerminalBundle.message("option.notrack.description"))
@@ -192,11 +190,9 @@ private fun ShellCommandContext.trackingOptions() {
}
argument {
suggestions(localBranchesGenerator)
isOptional = true
optional()
}
exclusiveOn = listOf(
"-t", "--track"
)
exclusiveOn(listOf("-t", "--track"))
}
}
@@ -206,7 +202,7 @@ private fun ShellArgumentContext.addHeadSuggestions() {
ShellCompletionSuggestion("HEAD", description = GitTerminalBundle.message("suggestion.head.description")),
ShellCompletionSuggestion("HEAD~<N>") {
description(GitTerminalBundle.message("suggestion.headn.description"))
insertValue = "HEAD~"
insertValue("HEAD~")
}
)
}
@@ -224,8 +220,8 @@ internal val gitOverrideSpec = ShellCommandSpec("git") {
suggestions(allBranchesGenerator)
isOptional = true
isVariadic = true
optional()
variadic()
}
}
@@ -237,8 +233,8 @@ internal val gitOverrideSpec = ShellCommandSpec("git") {
suggestions(allBranchesGenerator)
isOptional = true
isVariadic = true
optional()
variadic()
}
}
@@ -250,48 +246,47 @@ internal val gitOverrideSpec = ShellCommandSpec("git") {
}
suggestions(allBranchesGenerator)
suggestions(remotesGenerator)
isOptional = true
isVariadic = false
optional()
}
argument {
displayName(GitTerminalBundle.message("rebase.arg2.name"))
suggestions(localBranchesGenerator)
isOptional = true
optional()
}
}
subcommand("push") {
argument {
displayName(GitTerminalBundle.message("push.arg1.name"))
suggestions(remotesGenerator)
isOptional = true
optional()
}
argument {
displayName(GitTerminalBundle.message("push.arg2.name"))
suggestions(localBranchesGenerator)
isOptional = true
optional()
}
}
subcommand("pull") {
option("--rebase") {
separator = "="
separator("=")
description(GitTerminalBundle.message("pull.opt-rebase.description"))
argument {
displayName(GitTerminalBundle.message("pull.opt-rebase.arg1.name"))
suggestions("false", "true", "merges", "preserve", "interactive")
suggestions(remotesGenerator)
isOptional = true
optional()
}
}
argument {
displayName(GitTerminalBundle.message("pull.arg1.name"))
suggestions(remotesGenerator)
isOptional = true
optional()
}
argument {
displayName(GitTerminalBundle.message("pull.arg2.name"))
suggestions(localBranchesGenerator)
isOptional = true
optional()
}
}
subcommand("remote") {
@@ -319,16 +314,16 @@ internal val gitOverrideSpec = ShellCommandSpec("git") {
argument {
displayName(GitTerminalBundle.message("fetch.arg1.name"))
suggestions(remotesGenerator)
isOptional = true
optional()
}
argument {
displayName(GitTerminalBundle.message("fetch.arg2.name"))
suggestions(localBranchesGenerator)
isOptional = true
optional()
}
argument {
displayName(GitTerminalBundle.message("fetch.arg3.name"))
isOptional = true
optional()
}
}
subcommand("stash") {
@@ -340,7 +335,7 @@ internal val gitOverrideSpec = ShellCommandSpec("git") {
}
argument {
displayName(GitTerminalBundle.message("stash.branch.arg2.name"))
isOptional = true
optional()
}
}
}
@@ -356,7 +351,7 @@ internal val gitOverrideSpec = ShellCommandSpec("git") {
)
}
suggestions(localOrRemoteBranchesGenerator)
isVariadic = true
variadic()
}
}
option("-d", "--delete") {
@@ -369,7 +364,7 @@ internal val gitOverrideSpec = ShellCommandSpec("git") {
)
}
suggestions(localOrRemoteBranchesGenerator)
isVariadic = true
variadic()
}
}
option("-m", "--move") {
@@ -400,16 +395,16 @@ internal val gitOverrideSpec = ShellCommandSpec("git") {
argument {
displayName(GitTerminalBundle.message("branch.opt-set-upstream.arg1.name"))
suggestions(allBranchesGenerator)
isOptional = true
optional()
}
}
option("--set-upstream-to") {
description(GitTerminalBundle.message("branch.opt-set-upstream.description"))
separator = "="
separator("=")
argument {
displayName(GitTerminalBundle.message("branch.opt-set-upstream.arg1.name"))
suggestions(allBranchesGenerator)
isOptional = true
optional()
}
}
option("--unset-upstream") {
@@ -417,7 +412,7 @@ internal val gitOverrideSpec = ShellCommandSpec("git") {
argument {
displayName(GitTerminalBundle.message("branch.opt-unset-upstream.arg1.name"))
suggestions(localBranchesGenerator)
isOptional = true
optional()
}
}
trackingOptions()
@@ -438,15 +433,15 @@ internal val gitOverrideSpec = ShellCommandSpec("git") {
suggestions(allBranchesGenerator)
isOptional = true
optional()
}
argument {
displayName(GitTerminalBundle.message("checkout.arg2.name"))
suggestions(ShellDataGenerators.fileSuggestionsGenerator(false))
isVariadic = true
isOptional = true
variadic()
optional()
}
}
subcommand("merge") {
@@ -459,8 +454,8 @@ internal val gitOverrideSpec = ShellCommandSpec("git") {
suggestions(allBranchesGenerator)
isVariadic = true
isOptional = true
variadic()
optional()
}
}
subcommand("switch") {
@@ -477,7 +472,7 @@ internal val gitOverrideSpec = ShellCommandSpec("git") {
}
argument {
displayName(GitTerminalBundle.message("switch.arg2.name"))
isOptional = true
optional()
}
}
}
@@ -57,8 +57,8 @@ object ShellDataGenerators {
val type = if (it.type == ShellFileInfo.Type.DIRECTORY) ShellSuggestionType.FOLDER else ShellSuggestionType.FILE
val name = it.name + if (type == ShellSuggestionType.FOLDER) File.separator else ""
val suggestion = ShellCompletionSuggestion(name) {
this.type = type
this.prefixReplacementIndex = prefixReplacementIndex
type(type)
prefixReplacementIndex(prefixReplacementIndex)
}
if (type == ShellSuggestionType.FILE) {
listOf(suggestion)
@@ -67,9 +67,9 @@ object ShellDataGenerators {
// Directory suggestion has a trailing file separator, but suggestion without it is also valid.
// It is needed for the parser to consider it as a valid suggestion and not mark it as something unknown.
val hiddenSuggestion = ShellCompletionSuggestion(it.name) {
this.type = ShellSuggestionType.FOLDER
this.prefixReplacementIndex = prefixReplacementIndex
this.isHidden = true
type(ShellSuggestionType.FOLDER)
prefixReplacementIndex(prefixReplacementIndex)
hidden()
}
listOf(suggestion, hiddenSuggestion)
}
@@ -80,8 +80,8 @@ object ShellDataGenerators {
// It is needed for the parser to consider current typed prefix as a valid file suggestion.
return if (path.isNotEmpty() && path == adjustedPrefix) {
val emptySuggestion = ShellCompletionSuggestion("") {
this.prefixReplacementIndex = prefixReplacementIndex
this.isHidden = true
prefixReplacementIndex(prefixReplacementIndex)
hidden()
}
suggestions + emptySuggestion
}
@@ -29,28 +29,35 @@ sealed interface ShellArgumentContext {
fun displayName(supplier: Supplier<@Nls String>)
/**
* Whether this argument is not required to have a value.
* Specifies that this argument is not required to have a value.
*
* False by default (the argument is required).
* By default, the argument is required.
*/
fun optional()
@Deprecated("Please use optional() method instead")
var isOptional: Boolean
/**
* Specifies that this argument can be repeated infinitely.
* For example, `git add` takes a variadic argument of filenames.
*
* False by default.
* By default, the argument value can be specified only once.
*/
fun variadic()
@Deprecated("Please use variadic() method instead")
var isVariadic: Boolean
/**
* Specifies that the options can be placed between values of the variadic argument.
* For example, it is true for `git add` command.
* We can write like this: `git add file1 file2 -v file3`. Where `-v` is the option.
* Specifies that the options can't be placed between values of the variadic argument.
* By default, this behavior is allowed.
*
* True by default.
* For example, it is not allowed for `echo` command.
* If we write: `echo hello -n world`, `-n` will be considered as a variadic argument value rather than an option.
* So, we should write `echo -n hello world` instead.
*/
var optionsCanBreakVariadicArg: Boolean
fun optionsCantBreakVariadicArg()
/**
* Generate suggestions for the argument values.
@@ -11,13 +11,14 @@ import java.util.function.Supplier
/**
* Params [parentNames] and [argNumber] used to build cache key/debug name of the generators
*/
@Suppress("OVERRIDE_DEPRECATION")
internal class ShellArgumentContextImpl(
private val parentNames: List<String>,
private val argNumber: Int
) : ShellArgumentContext {
override var isOptional: Boolean = false
override var isVariadic: Boolean = false
override var optionsCanBreakVariadicArg: Boolean = true
private var optionsCanBreakVariadicArg: Boolean = true
private var displayNameSupplier: Supplier<String>? = null
@@ -29,6 +30,18 @@ internal class ShellArgumentContextImpl(
displayNameSupplier = supplier
}
override fun optional() {
isOptional = true
}
override fun variadic() {
isVariadic = true
}
override fun optionsCantBreakVariadicArg() {
optionsCanBreakVariadicArg = false
}
private val generators: MutableList<ShellRuntimeDataGenerator<List<ShellCompletionSuggestion>>> = mutableListOf()
override fun suggestions(content: suspend (ShellRuntimeContext) -> List<ShellCompletionSuggestion>) {
@@ -42,7 +55,7 @@ internal class ShellArgumentContextImpl(
override fun suggestions(vararg names: String) {
val generator = ShellRuntimeDataGenerator(debugName = createCacheKey()) {
names.map { ShellCompletionSuggestion(it) { type = ShellSuggestionType.ARGUMENT } }
names.map { ShellCompletionSuggestion(it) { type(ShellSuggestionType.ARGUMENT) } }
}
generators.add(generator)
}
@@ -17,17 +17,15 @@ import org.jetbrains.annotations.ApiStatus
@ShellCommandSpecDsl
sealed interface ShellCommandContext : ShellSuggestionContext {
/**
* Whether this command can't be executed without mentioning the subcommand.
*
* False by default.
* Specifies that this command can't be executed without mentioning the subcommand.
*/
var requiresSubcommand: Boolean
fun requiresSubcommand()
/**
* Allows modifying default parser options.
* @see [ShellCommandParserOptions]
*/
var parserOptions: ShellCommandParserOptions
fun parserOptions(options: ShellCommandParserOptions)
/**
* Specify the subcommands of the current command.
@@ -62,7 +60,7 @@ sealed interface ShellCommandContext : ShellSuggestionContext {
/**
* Specifies that this Shell command should have an argument.
* Note that arguments are not optional by default.
* If your argument is not necessary to be specified, then set [ShellArgumentContext.isOptional] to true.
* If your argument is not necessary to be specified, specify [ShellArgumentContext.optional].
* Arguments should be defined in the same order as it is expected in the command line.
* @param [content] description of the argument
*/
@@ -14,8 +14,8 @@ internal class ShellCommandContextImpl(
names: List<String>,
private val parentNames: List<String> = emptyList()
) : ShellSuggestionContextBase(names), ShellCommandContext {
override var requiresSubcommand: Boolean = false
override var parserOptions: ShellCommandParserOptions = ShellCommandParserOptions.DEFAULT
private var requiresSubcommand: Boolean = false
private var parserOptions: ShellCommandParserOptions = ShellCommandParserOptions.DEFAULT
private var subcommandSuppliers: MutableList<suspend (ShellRuntimeContext) -> List<ShellCommandSpec>> = mutableListOf()
private var dynamicOptionSuppliers: MutableList<suspend (ShellRuntimeContext) -> List<ShellOptionSpec>> = mutableListOf()
@@ -24,6 +24,14 @@ internal class ShellCommandContextImpl(
private val parentNamesWithSelf: List<String> = parentNames + names.first()
override fun requiresSubcommand() {
requiresSubcommand = true
}
override fun parserOptions(options: ShellCommandParserOptions) {
parserOptions = options
}
override fun subcommands(content: suspend ShellChildCommandsContext.(ShellRuntimeContext) -> Unit) {
val supplier: suspend (ShellRuntimeContext) -> List<ShellCommandSpec> = { shellContext ->
val context = ShellChildCommandsContextImpl(parentNamesWithSelf)
@@ -15,7 +15,7 @@ sealed interface ShellCompletionSuggestionContext : ShellSuggestionContext {
/**
* Used for now only to automatically configure the icon.
*/
var type: ShellSuggestionType
fun type(type: ShellSuggestionType)
/**
* Custom icon instead of autodetected from [type].
@@ -30,15 +30,15 @@ sealed interface ShellCompletionSuggestionContext : ShellSuggestionContext {
* 2. If typed prefix is `foo/b` and suggestion name is `bar` (we want to suggest the part of the path after `/`),
* then the replacement index should be `4`.
*/
var prefixReplacementIndex: Int
fun prefixReplacementIndex(index: Int)
/**
* If true, then this suggestion won't be shown in the completion popup.
* It may be needed to specify that this suggestion is also a valid value for the argument.
* Marks this suggestion to not show it in the completion popup.
* It may be necessary to specify that this suggestion is also a valid value for the argument.
* So parser will be able to distinguish it and not mark it as something unknown.
*
* For example, if there is a directory suggestion, then it may have a trailing file separator or may not.
* Both options are acceptable, but only one of them should be shown in the completion popup.
*/
var isHidden: Boolean
fun hidden()
}
@@ -11,15 +11,27 @@ import javax.swing.Icon
internal class ShellCompletionSuggestionContextImpl(
private val name: String,
) : ShellSuggestionContextBase(listOf(name)), ShellCompletionSuggestionContext {
override var type: ShellSuggestionType = ShellSuggestionType.ARGUMENT
private var type: ShellSuggestionType = ShellSuggestionType.ARGUMENT
private var icon: Icon? = null
override var prefixReplacementIndex: Int = 0
override var isHidden: Boolean = false
private var prefixReplacementIndex: Int = 0
private var isHidden: Boolean = false
override fun type(type: ShellSuggestionType) {
this.type = type
}
override fun icon(icon: Icon) {
this.icon = icon
}
override fun prefixReplacementIndex(index: Int) {
this.prefixReplacementIndex = index
}
override fun hidden() {
isHidden = true
}
fun build(): ShellCompletionSuggestion {
return ShellCompletionSuggestionImpl(
name = name,
@@ -16,56 +16,47 @@ import org.jetbrains.annotations.ApiStatus
@ShellCommandSpecDsl
sealed interface ShellOptionContext : ShellSuggestionContext {
/**
* Whether this option can be available for all subcommands of the current Shell command.
*
* False by default.
* Makes this option available for all subcommands of the current Shell command.
*/
var isPersistent: Boolean
fun persistent()
/**
* Whether this option always must be present in the Shell command call.
*
* False by default.
* Specifies that this option always must be present in the Shell command call.
*/
var isRequired: Boolean
fun required()
/**
* The separator between the option name and the argument value (if option has an argument).
* Specifies the separator between the option name and the argument value (if the option has an argument).
* For example, in case of `--opt=value`, the separator should be `=`.
*
* Whitespace is used as a separator by default (but the value of the property is null in this case).
* Whitespace is used as a separator by default.
*/
var separator: String?
fun separator(separator: String)
/**
* The maximum amount of times this option can be present in the command line.
* Specifies the maximum number of times this option can be present in the command line.
* Zero value means that it can be repeated infinitely.
* If the option is already present this number of times in the command, it won't be shown in the completion popup anymore.
*
* One by default.
*/
var repeatTimes: Int
fun repeatTimes(times: Int)
/**
* Names of the options with those this option cannot be used.
* Specifies the names of the options with those this option cannot be used.
* If any of such options is used in the command, this option won't be shown in the completion popup.
*
* Empty list by default.
*/
var exclusiveOn: List<String>
fun exclusiveOn(on: List<String>)
/**
* Names of the options required to use this option.
* Specifies names of the options required to use this option.
* Until all such options are used in the command, this option won't be shown in the completion popup.
*
* Empty list by default.
*/
var dependsOn: List<String>
fun dependsOn(on: List<String>)
/**
* Specifies that this Shell option should have an argument.
* Note that arguments are not optional by default.
* If your argument is not necessary to be specified, then set [ShellArgumentContext.isOptional] to true.
* If your argument is not necessary to be specified, specify [ShellArgumentContext.optional].
* Arguments should be defined in the same order as it is expected in the command line.
* @param [content] description of the argument
*/
@@ -12,15 +12,39 @@ internal class ShellOptionContextImpl(
names: List<String>,
private val parentCommandNames: List<String>
) : ShellSuggestionContextBase(names), ShellOptionContext {
override var isPersistent: Boolean = false
override var isRequired: Boolean = false
override var separator: String? = null
override var repeatTimes: Int = 1
override var exclusiveOn: List<String> = emptyList()
override var dependsOn: List<String> = emptyList()
private var isPersistent: Boolean = false
private var isRequired: Boolean = false
private var separator: String? = null
private var repeatTimes: Int = 1
private var exclusiveOn: List<String> = emptyList()
private var dependsOn: List<String> = emptyList()
private val arguments: MutableList<ShellArgumentSpec> = mutableListOf()
override fun persistent() {
isPersistent = true
}
override fun required() {
isRequired = true
}
override fun separator(separator: String) {
this.separator = separator
}
override fun repeatTimes(times: Int) {
repeatTimes = times
}
override fun exclusiveOn(on: List<String>) {
exclusiveOn = on
}
override fun dependsOn(on: List<String>) {
dependsOn = on
}
override fun argument(content: ShellArgumentContext.() -> Unit) {
val context = ShellArgumentContextImpl(parentCommandNames + names.first(), argNumber = arguments.size + 1)
content.invoke(context)
@@ -3,7 +3,6 @@ package org.jetbrains.plugins.terminal.block.completion.spec.dsl
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.annotations.Nls
import org.jetbrains.annotations.NonNls
import java.util.function.Supplier
/**
@@ -12,12 +11,10 @@ import java.util.function.Supplier
@ApiStatus.Experimental
@ShellCommandSpecDsl
sealed interface ShellSuggestionContext {
val names: List<@NonNls String>
/**
* The string to be shown in the completion popup instead of command/option name.
* Sets the string to be shown in the completion popup instead of command/option name.
*/
var displayName: String?
fun displayName(name: String)
/**
* Text to be shown in the documentation popup for this command/option.
@@ -34,12 +31,15 @@ sealed interface ShellSuggestionContext {
* Supports specifying caret position after completion item insertion in a form `some{caret}item`.
* In this example `someitem` text will be inserted and caret is placed between `some` and `item`.
*/
var insertValue: String?
fun insertValue(value: String)
@set:Deprecated("Please use priority() method instead")
var priority: Int
/**
* Int from 0 to 100 with default 50.
* Allows specifying the order of the items in the completion popup.
* The greater the number, the closer the item will be to the first place.
*/
var priority: Int
fun priority(priority: Int)
}
@@ -5,10 +5,12 @@ import org.jetbrains.annotations.Nls
import java.util.function.Supplier
internal abstract class ShellSuggestionContextBase(
final override val names: List<String>,
protected val names: List<String>,
) : ShellSuggestionContext {
override var displayName: String? = null
override var insertValue: String? = null
protected var displayName: String? = null
protected var insertValue: String? = null
@Suppress("OVERRIDE_DEPRECATION")
override var priority: Int = 50
set(value) {
if (value in 0..100) {
@@ -19,6 +21,10 @@ internal abstract class ShellSuggestionContextBase(
protected var descriptionSupplier: Supplier<@Nls String>? = null
override fun displayName(name: String) {
displayName = name
}
override fun description(text: String) {
descriptionSupplier = Supplier { text }
}
@@ -27,6 +33,14 @@ internal abstract class ShellSuggestionContextBase(
descriptionSupplier = supplier
}
override fun insertValue(value: String) {
this.insertValue = value
}
override fun priority(priority: Int) {
this.priority = priority
}
init {
if (names.isEmpty()) {
error("At least one name must be provided")
@@ -39,11 +39,11 @@ internal class ShellJsonBasedArgumentSpec(
s.names.map { name ->
// TODO: there should be a way to localize the json-based descriptions
ShellCompletionSuggestion(name) {
type = ShellSuggestionType.ARGUMENT
displayName = s.displayName
if (s.description != null) description(s.description!!)
insertValue = s.insertValue
priority = s.priority
type(ShellSuggestionType.ARGUMENT)
s.displayName?.let { displayName(it) }
s.description?.let { description(it) }
s.insertValue?.let { insertValue(it) }
priority(s.priority)
}
}
}
@@ -25,21 +25,21 @@ internal fun cdCommandSpec() = ShellCommandSpec("cd") {
val replacementIndex = path.length + if (context.typedPrefix.startsWith('"')) 1 else 0
val suggestions = directories.flatMap {
val suggestion = ShellCompletionSuggestion(it.name + File.separator) {
type = ShellSuggestionType.FOLDER
prefixReplacementIndex = replacementIndex
type(ShellSuggestionType.FOLDER)
prefixReplacementIndex(replacementIndex)
}
val hiddenSuggestion = ShellCompletionSuggestion(it.name) {
type = ShellSuggestionType.FOLDER
prefixReplacementIndex = replacementIndex
isHidden = true
type(ShellSuggestionType.FOLDER)
prefixReplacementIndex(replacementIndex)
hidden()
}
listOf(suggestion, hiddenSuggestion)
}
val adjustedPrefix = context.typedPrefix.removePrefix("\"").removeSuffix("'")
if (path.isNotEmpty() && path == adjustedPrefix) {
val emptySuggestion = ShellCompletionSuggestion("") {
prefixReplacementIndex = replacementIndex
isHidden = true
prefixReplacementIndex(replacementIndex)
hidden()
}
suggestions + emptySuggestion
}
@@ -19,7 +19,7 @@ object ShellMakeCommandSpec {
fun create(): ShellCommandSpec = ShellCommandSpec("make") {
description(TerminalBundle.messagePointer("make.command.description"))
argument {
isVariadic = true
variadic()
displayName(TerminalBundle.messagePointer("make.command.arg.displayName"))
@@ -52,7 +52,7 @@ object ShellMakeCommandSpec {
.mapNotNull { MakefileTarget.parse(it) }
.map { makefileTarget ->
ShellCompletionSuggestion(makefileTarget.name) {
type = ShellSuggestionType.ARGUMENT
type(ShellSuggestionType.ARGUMENT)
@Suppress("HardCodedStringLiteral")
val desc = listOfNotNull(
makefileTarget.comment
@@ -50,18 +50,18 @@ internal class ShellCommandSpecSuggestionsTest(private val engine: TerminalEngin
private val spec = ShellCommandSpec(commandName) {
option("-a", "--asd")
option("--bcde") {
isPersistent = true
repeatTimes = 2
persistent()
repeatTimes(2)
}
option("--argum") {
argument {
isOptional = true
optional()
suggestions("all", "none", "default")
}
}
argument {
isOptional = true
optional()
suggestions("abc")
}
@@ -69,7 +69,7 @@ internal class ShellCommandSpecSuggestionsTest(private val engine: TerminalEngin
subcommand("sub") {
option("-o", "--opt1")
option("-a") {
repeatTimes = 0
repeatTimes(0)
}
option("--long")
option("--withReqArg") {
@@ -79,7 +79,7 @@ internal class ShellCommandSpecSuggestionsTest(private val engine: TerminalEngin
}
option("--withOptArg") {
argument {
isOptional = true
optional()
}
}
@@ -87,26 +87,26 @@ internal class ShellCommandSpecSuggestionsTest(private val engine: TerminalEngin
suggestions("file")
}
argument {
isOptional = true
optional()
suggestions("s1")
}
}
subcommand("excl") {
option("-a") {
exclusiveOn = listOf("-b")
exclusiveOn(listOf("-b"))
}
option("-b") {
exclusiveOn = listOf("-a")
exclusiveOn(listOf("-a"))
}
option("-c")
option("-d") {
dependsOn = listOf("-a", "-c")
dependsOn(listOf("-a", "-c"))
}
}
subcommand("reqSub") {
requiresSubcommand = true
requiresSubcommand()
subcommands {
subcommand("abc")
}
@@ -115,14 +115,14 @@ internal class ShellCommandSpecSuggestionsTest(private val engine: TerminalEngin
subcommand("manyArgs") {
argument {
isOptional = true
optional()
suggestions("arg1")
}
argument {
suggestions("arg2", "arg22")
}
argument {
isOptional = true
optional()
suggestions("arg3")
}
argument {
@@ -131,13 +131,15 @@ internal class ShellCommandSpecSuggestionsTest(private val engine: TerminalEngin
}
subcommand("optPrecedeArgs") {
parserOptions = ShellCommandParserOptions.builder()
.optionsMustPrecedeArguments(true)
.build()
parserOptions(
ShellCommandParserOptions.builder()
.optionsMustPrecedeArguments(true)
.build()
)
option("-c")
option("-d")
argument {
isOptional = true
optional()
suggestions("arg")
}
@@ -156,7 +158,7 @@ internal class ShellCommandSpecSuggestionsTest(private val engine: TerminalEngin
option("-a")
option("--var") {
argument {
isVariadic = true
variadic()
suggestions("var1", "var2")
}
}
@@ -164,11 +166,11 @@ internal class ShellCommandSpecSuggestionsTest(private val engine: TerminalEngin
suggestions("req")
}
argument {
isVariadic = true
variadic()
suggestions("v")
}
argument {
isOptional = true
optional()
suggestions("opt")
}
}
@@ -177,14 +179,14 @@ internal class ShellCommandSpecSuggestionsTest(private val engine: TerminalEngin
option("-b")
option("---") {
argument {
isVariadic = true
optionsCanBreakVariadicArg = false
variadic()
optionsCantBreakVariadicArg()
suggestions("var")
}
}
argument {
isVariadic = true
optionsCanBreakVariadicArg = false
variadic()
optionsCantBreakVariadicArg()
suggestions("v")
}
argument {
@@ -209,17 +211,17 @@ internal class ShellCommandSpecSuggestionsTest(private val engine: TerminalEngin
option("--opt") {
argument {
suggestions("3", "4", "5")
isOptional = true
optional()
}
}
argument {
suggestions("1", "2", "3")
isOptional = true
optional()
}
argument {
suggestions("2", "3", "4")
isOptional = true
optional()
}
}
@@ -44,13 +44,13 @@ internal class ShellCommandTreeBuilderTest(private val engine: TerminalEngine) {
option("--bcde")
option("--argum") {
argument {
isOptional = true
optional()
suggestions("someArg")
}
}
argument {
isOptional = true
optional()
suggestions("aaa", "bbb")
}
@@ -61,7 +61,7 @@ internal class ShellCommandTreeBuilderTest(private val engine: TerminalEngine) {
option("--manyArgs") {
argument()
argument {
isOptional = true
optional()
suggestions("a2")
}
}
@@ -71,7 +71,7 @@ internal class ShellCommandTreeBuilderTest(private val engine: TerminalEngine) {
option("-a")
argument {
isOptional = true
optional()
suggestions {
listOf(
ShellAliasSuggestion("alias-1", "al --manyArgs somearg"),
@@ -93,23 +93,23 @@ internal class ShellCommandTreeBuilderTest(private val engine: TerminalEngine) {
}
option("--withOptArg") {
argument {
isOptional = true
optional()
}
}
option("--manyArgs") {
argument()
argument {
isOptional = true
optional()
suggestions("a2")
}
}
option("--skippedArg") {
argument {
isOptional = true
optional()
}
argument()
argument {
isOptional = true
optional()
suggestions("opt2")
}
}
@@ -118,22 +118,24 @@ internal class ShellCommandTreeBuilderTest(private val engine: TerminalEngine) {
suggestions("somePath")
}
argument {
isOptional = true
optional()
suggestions("arg1", "arg2")
}
}
subcommand("nonPosix") {
parserOptions = ShellCommandParserOptions.builder()
.flagsArePosixNonCompliant(true)
.build()
parserOptions(
ShellCommandParserOptions.builder()
.flagsArePosixNonCompliant(true)
.build()
)
option("-a")
option("-b")
}
subcommand("sep") {
option("--withSeparator") {
separator = "="
separator("=")
argument()
}
}
@@ -31,14 +31,14 @@ class TerminalCompletionPopupTest : BasePlatformTestCase() {
subcommand("sync")
subcommand("show") {
argument {
isOptional = true
optional()
suggestions("roots", "files", "statuses")
}
}
subcommand("start") {
argument {
isOptional = true
optional()
suggestions("platform/", "platform-ui/", "shared\\", "shared-ui\\")
}
}
@@ -58,8 +58,8 @@ class TerminalCompletionPopupTest : BasePlatformTestCase() {
}
}
val abPriority = ShellCompletionSuggestion("ab") { priority = 100 }
val acPriority = ShellCompletionSuggestion("ac") { priority = 100 }
val abPriority = ShellCompletionSuggestion("ab") { priority(100) }
val acPriority = ShellCompletionSuggestion("ac") { priority(100) }
listOf(abPriority, acPriority) + items.map { ShellCompletionSuggestion(it) }
}
}