Skip to content

Commit

Permalink
Add completion and no-op for new command types (#556)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajalt authored Sep 20, 2024
1 parent 2f36e93 commit 6812854
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 1 deletion.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Changelog

## Unreleased
### Added
- Added completion commands for suspending and chained commands. ([#553](https://github.com/ajalt/clikt/pull/553))
- Added no-op suspending commands. ([#554](https://github.com/ajalt/clikt/pull/554))

## 5.0.0
### Added
Expand Down
7 changes: 7 additions & 0 deletions clikt-mordant/api/clikt-mordant.api
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ public final class com/github/ajalt/clikt/command/SuspendingCliktCommandKt {
public static synthetic fun test$default (Lcom/github/ajalt/clikt/command/SuspendingCliktCommand;[Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;ZLcom/github/ajalt/mordant/rendering/AnsiLevel;IIZZZLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
}

public abstract class com/github/ajalt/clikt/command/SuspendingNoOpCliktCommand : com/github/ajalt/clikt/command/SuspendingCliktCommand {
public fun <init> ()V
public fun <init> (Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun run (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract class com/github/ajalt/clikt/core/CliktCommand : com/github/ajalt/clikt/core/CoreCliktCommand {
public fun <init> ()V
public fun <init> (Ljava/lang/String;)V
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.github.ajalt.clikt.command

/**
* A [SuspendingCliktCommand] that has a default implementation of
* [run][SuspendingCliktCommand.run] that is a no-op.
*/
abstract class SuspendingNoOpCliktCommand(
/**
* The name of the program to use in the help output. If not given, it is inferred from the
* class name.
*/
name: String? = null,
) : SuspendingCliktCommand(name) {
override suspend fun run() = Unit
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.github.ajalt.clikt.core

/**
* A [CoreCliktCommand] that has a default implementation of [CliktCommand.run] that is a no-op.
* A [CliktCommand] that has a default implementation of [CliktCommand.run] that is a no-op.
*/
open class NoOpCliktCommand(
/**
Expand Down
25 changes: 25 additions & 0 deletions clikt/api/clikt.api
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,22 @@ public final class com/github/ajalt/clikt/command/CoreSuspendingCliktCommandKt {
public static final fun parse (Lcom/github/ajalt/clikt/command/CoreSuspendingCliktCommand;[Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public abstract class com/github/ajalt/clikt/command/CoreSuspendingNoOpCliktCommand : com/github/ajalt/clikt/command/CoreSuspendingCliktCommand {
public fun <init> ()V
public fun <init> (Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun run (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/github/ajalt/clikt/completion/ChainedCompletionCommand : com/github/ajalt/clikt/command/CoreChainedCliktCommand {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun help (Lcom/github/ajalt/clikt/core/Context;)Ljava/lang/String;
public fun helpEpilog (Lcom/github/ajalt/clikt/core/Context;)Ljava/lang/String;
public fun run (Ljava/lang/Object;)Ljava/lang/Object;
}

public final class com/github/ajalt/clikt/completion/CompletionBuiltinsKt {
public static final fun completionOption (Lcom/github/ajalt/clikt/core/BaseCliktCommand;[Ljava/lang/String;Ljava/lang/String;Z)Lcom/github/ajalt/clikt/core/BaseCliktCommand;
public static synthetic fun completionOption$default (Lcom/github/ajalt/clikt/core/BaseCliktCommand;[Ljava/lang/String;Ljava/lang/String;ZILjava/lang/Object;)Lcom/github/ajalt/clikt/core/BaseCliktCommand;
Expand Down Expand Up @@ -112,6 +128,15 @@ public final class com/github/ajalt/clikt/completion/CompletionGenerator {
public final fun generateCompletionForCommand (Lcom/github/ajalt/clikt/core/BaseCliktCommand;Ljava/lang/String;)Ljava/lang/String;
}

public final class com/github/ajalt/clikt/completion/SuspendingCompletionCommand : com/github/ajalt/clikt/command/CoreSuspendingCliktCommand {
public fun <init> ()V
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public fun help (Lcom/github/ajalt/clikt/core/Context;)Ljava/lang/String;
public fun helpEpilog (Lcom/github/ajalt/clikt/core/Context;)Ljava/lang/String;
public fun run (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
}

public final class com/github/ajalt/clikt/core/Abort : com/github/ajalt/clikt/core/ProgramResult {
public fun <init> ()V
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ abstract class CoreChainedCliktCommand<T>(
}



/**
* Parse the command line and print helpful output if any errors occur.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.github.ajalt.clikt.command

/**
* A [CoreSuspendingCliktCommand] that has a default implementation of
* [run][CoreSuspendingCliktCommand.run] that is a no-op.
*/
abstract class CoreSuspendingNoOpCliktCommand(
/**
* The name of the program to use in the help output. If not given, it is inferred from the
* class name.
*/
name: String? = null,
) : CoreSuspendingCliktCommand(name) {
override suspend fun run() = Unit
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.github.ajalt.clikt.completion

import com.github.ajalt.clikt.command.CoreChainedCliktCommand
import com.github.ajalt.clikt.command.CoreSuspendingCliktCommand
import com.github.ajalt.clikt.completion.CompletionGenerator.generateCompletionForCommand
import com.github.ajalt.clikt.core.BaseCliktCommand
import com.github.ajalt.clikt.core.Context
Expand Down Expand Up @@ -44,3 +46,40 @@ class CompletionCommand(
throw PrintCompletionMessage(generateCompletionForCommand(cmd, shell))
}
}

/**
* A [CoreSuspendingCliktCommand] subcommand that will print a completion script for the given shell
* when invoked.
*/
class SuspendingCompletionCommand(
private val help: String = "Generate a tab-complete script for the given shell",
private val epilog: String = "",
name: String = "generate-completion",
) : CoreSuspendingCliktCommand(name) {
override fun help(context: Context): String = help
override fun helpEpilog(context: Context): String = epilog
private val shell by argument("shell").choice(*choices)
override suspend fun run() {
val cmd = currentContext.parent?.command ?: this
throw PrintCompletionMessage(generateCompletionForCommand(cmd, shell))
}
}

/**
* A [CoreChainedCliktCommand] subcommand that will print a completion script for the given shell
* when invoked.
*/
class ChainedCompletionCommand<T>(
private val help: String = "Generate a tab-complete script for the given shell",
private val epilog: String = "",
name: String = "generate-completion",
) : CoreChainedCliktCommand<T>(name) {
override fun help(context: Context): String = help
override fun helpEpilog(context: Context): String = epilog
private val shell by argument("shell").choice(*choices)
override fun run(value: T): T {
val cmd = currentContext.parent?.command ?: this
throw PrintCompletionMessage(generateCompletionForCommand(cmd, shell))
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,13 @@ class SuspendingCliktCommandTest {

C().test("baz").output shouldBe "baz\n"
}

@Test
@JsName("suspending_noop_command_test")
fun `suspending no-op command test`() = runTest {
class C : SuspendingNoOpCliktCommand()
class Sub : CoreSuspendingNoOpCliktCommand()

C().subcommands(Sub()).test("sub").output shouldBe ""
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.github.ajalt.clikt.completion

import com.github.ajalt.clikt.command.ChainedCliktCommand
import com.github.ajalt.clikt.command.SuspendingNoOpCliktCommand
import com.github.ajalt.clikt.command.parse
import com.github.ajalt.clikt.core.PrintCompletionMessage
import com.github.ajalt.clikt.core.context
import com.github.ajalt.clikt.core.subcommands
Expand All @@ -11,6 +14,7 @@ import com.github.ajalt.clikt.testing.TestCommand
import com.github.ajalt.clikt.testing.parse
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.string.shouldContain
import kotlinx.coroutines.test.runTest
import kotlin.js.JsName
import kotlin.test.Test
import kotlin.test.assertEquals
Expand Down Expand Up @@ -130,4 +134,34 @@ abstract class CompletionTestBase(private val shell: String) {
message shouldContain shell
message shouldContain "foo"
}

@Test
@JsName("suspending_completion_command")
fun `suspending completion command`() = runTest {
class Foo : SuspendingNoOpCliktCommand()

val message = shouldThrow<PrintCompletionMessage> {
Foo()
.subcommands(SuspendingCompletionCommand(), Foo())
.parse(listOf("generate-completion", shell))
}.message
message shouldContain shell
message shouldContain "foo"
}

@Test
@JsName("chained_completion_command")
fun `chained completion command`() = runTest {
class Foo : ChainedCliktCommand<Unit>() {
override fun run(value: Unit) = Unit
}

val message = shouldThrow<PrintCompletionMessage> {
Foo()
.subcommands(ChainedCompletionCommand(), Foo())
.parse(listOf("generate-completion", shell), Unit)
}.message
message shouldContain shell
message shouldContain "foo"
}
}

0 comments on commit 6812854

Please sign in to comment.