Skip to content

Commit 6812854

Browse files
authored
Add completion and no-op for new command types (#556)
1 parent 2f36e93 commit 6812854

File tree

10 files changed

+149
-1
lines changed

10 files changed

+149
-1
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
# Changelog
22

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

58
## 5.0.0
69
### Added

clikt-mordant/api/clikt-mordant.api

100755100644
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,13 @@ public final class com/github/ajalt/clikt/command/SuspendingCliktCommandKt {
2828
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;
2929
}
3030

31+
public abstract class com/github/ajalt/clikt/command/SuspendingNoOpCliktCommand : com/github/ajalt/clikt/command/SuspendingCliktCommand {
32+
public fun <init> ()V
33+
public fun <init> (Ljava/lang/String;)V
34+
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
35+
public fun run (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
36+
}
37+
3138
public abstract class com/github/ajalt/clikt/core/CliktCommand : com/github/ajalt/clikt/core/CoreCliktCommand {
3239
public fun <init> ()V
3340
public fun <init> (Ljava/lang/String;)V
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.github.ajalt.clikt.command
2+
3+
/**
4+
* A [SuspendingCliktCommand] that has a default implementation of
5+
* [run][SuspendingCliktCommand.run] that is a no-op.
6+
*/
7+
abstract class SuspendingNoOpCliktCommand(
8+
/**
9+
* The name of the program to use in the help output. If not given, it is inferred from the
10+
* class name.
11+
*/
12+
name: String? = null,
13+
) : SuspendingCliktCommand(name) {
14+
override suspend fun run() = Unit
15+
}

clikt-mordant/src/commonMain/kotlin/com/github/ajalt/clikt/core/NoOpCliktCommand.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.github.ajalt.clikt.core
22

33
/**
4-
* A [CoreCliktCommand] that has a default implementation of [CliktCommand.run] that is a no-op.
4+
* A [CliktCommand] that has a default implementation of [CliktCommand.run] that is a no-op.
55
*/
66
open class NoOpCliktCommand(
77
/**

clikt/api/clikt.api

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,22 @@ public final class com/github/ajalt/clikt/command/CoreSuspendingCliktCommandKt {
2626
public static final fun parse (Lcom/github/ajalt/clikt/command/CoreSuspendingCliktCommand;[Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
2727
}
2828

29+
public abstract class com/github/ajalt/clikt/command/CoreSuspendingNoOpCliktCommand : com/github/ajalt/clikt/command/CoreSuspendingCliktCommand {
30+
public fun <init> ()V
31+
public fun <init> (Ljava/lang/String;)V
32+
public synthetic fun <init> (Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
33+
public fun run (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
34+
}
35+
36+
public final class com/github/ajalt/clikt/completion/ChainedCompletionCommand : com/github/ajalt/clikt/command/CoreChainedCliktCommand {
37+
public fun <init> ()V
38+
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
39+
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
40+
public fun help (Lcom/github/ajalt/clikt/core/Context;)Ljava/lang/String;
41+
public fun helpEpilog (Lcom/github/ajalt/clikt/core/Context;)Ljava/lang/String;
42+
public fun run (Ljava/lang/Object;)Ljava/lang/Object;
43+
}
44+
2945
public final class com/github/ajalt/clikt/completion/CompletionBuiltinsKt {
3046
public static final fun completionOption (Lcom/github/ajalt/clikt/core/BaseCliktCommand;[Ljava/lang/String;Ljava/lang/String;Z)Lcom/github/ajalt/clikt/core/BaseCliktCommand;
3147
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;
@@ -112,6 +128,15 @@ public final class com/github/ajalt/clikt/completion/CompletionGenerator {
112128
public final fun generateCompletionForCommand (Lcom/github/ajalt/clikt/core/BaseCliktCommand;Ljava/lang/String;)Ljava/lang/String;
113129
}
114130

131+
public final class com/github/ajalt/clikt/completion/SuspendingCompletionCommand : com/github/ajalt/clikt/command/CoreSuspendingCliktCommand {
132+
public fun <init> ()V
133+
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
134+
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
135+
public fun help (Lcom/github/ajalt/clikt/core/Context;)Ljava/lang/String;
136+
public fun helpEpilog (Lcom/github/ajalt/clikt/core/Context;)Ljava/lang/String;
137+
public fun run (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
138+
}
139+
115140
public final class com/github/ajalt/clikt/core/Abort : com/github/ajalt/clikt/core/ProgramResult {
116141
public fun <init> ()V
117142
}

clikt/src/commonMain/kotlin/com/github/ajalt/clikt/command/CoreChainedCliktCommand.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ abstract class CoreChainedCliktCommand<T>(
3131
}
3232

3333

34+
3435
/**
3536
* Parse the command line and print helpful output if any errors occur.
3637
*
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.github.ajalt.clikt.command
2+
3+
/**
4+
* A [CoreSuspendingCliktCommand] that has a default implementation of
5+
* [run][CoreSuspendingCliktCommand.run] that is a no-op.
6+
*/
7+
abstract class CoreSuspendingNoOpCliktCommand(
8+
/**
9+
* The name of the program to use in the help output. If not given, it is inferred from the
10+
* class name.
11+
*/
12+
name: String? = null,
13+
) : CoreSuspendingCliktCommand(name) {
14+
override suspend fun run() = Unit
15+
}

clikt/src/commonMain/kotlin/com/github/ajalt/clikt/completion/CompletionBuiltins.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.github.ajalt.clikt.completion
22

3+
import com.github.ajalt.clikt.command.CoreChainedCliktCommand
4+
import com.github.ajalt.clikt.command.CoreSuspendingCliktCommand
35
import com.github.ajalt.clikt.completion.CompletionGenerator.generateCompletionForCommand
46
import com.github.ajalt.clikt.core.BaseCliktCommand
57
import com.github.ajalt.clikt.core.Context
@@ -44,3 +46,40 @@ class CompletionCommand(
4446
throw PrintCompletionMessage(generateCompletionForCommand(cmd, shell))
4547
}
4648
}
49+
50+
/**
51+
* A [CoreSuspendingCliktCommand] subcommand that will print a completion script for the given shell
52+
* when invoked.
53+
*/
54+
class SuspendingCompletionCommand(
55+
private val help: String = "Generate a tab-complete script for the given shell",
56+
private val epilog: String = "",
57+
name: String = "generate-completion",
58+
) : CoreSuspendingCliktCommand(name) {
59+
override fun help(context: Context): String = help
60+
override fun helpEpilog(context: Context): String = epilog
61+
private val shell by argument("shell").choice(*choices)
62+
override suspend fun run() {
63+
val cmd = currentContext.parent?.command ?: this
64+
throw PrintCompletionMessage(generateCompletionForCommand(cmd, shell))
65+
}
66+
}
67+
68+
/**
69+
* A [CoreChainedCliktCommand] subcommand that will print a completion script for the given shell
70+
* when invoked.
71+
*/
72+
class ChainedCompletionCommand<T>(
73+
private val help: String = "Generate a tab-complete script for the given shell",
74+
private val epilog: String = "",
75+
name: String = "generate-completion",
76+
) : CoreChainedCliktCommand<T>(name) {
77+
override fun help(context: Context): String = help
78+
override fun helpEpilog(context: Context): String = epilog
79+
private val shell by argument("shell").choice(*choices)
80+
override fun run(value: T): T {
81+
val cmd = currentContext.parent?.command ?: this
82+
throw PrintCompletionMessage(generateCompletionForCommand(cmd, shell))
83+
}
84+
}
85+

test/src/commonTest/kotlin/com/github/ajalt/clikt/command/SuspendingCliktCommandTest.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,13 @@ class SuspendingCliktCommandTest {
5252

5353
C().test("baz").output shouldBe "baz\n"
5454
}
55+
56+
@Test
57+
@JsName("suspending_noop_command_test")
58+
fun `suspending no-op command test`() = runTest {
59+
class C : SuspendingNoOpCliktCommand()
60+
class Sub : CoreSuspendingNoOpCliktCommand()
61+
62+
C().subcommands(Sub()).test("sub").output shouldBe ""
63+
}
5564
}

test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/CompletionTestBase.kt

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.github.ajalt.clikt.completion
22

3+
import com.github.ajalt.clikt.command.ChainedCliktCommand
4+
import com.github.ajalt.clikt.command.SuspendingNoOpCliktCommand
5+
import com.github.ajalt.clikt.command.parse
36
import com.github.ajalt.clikt.core.PrintCompletionMessage
47
import com.github.ajalt.clikt.core.context
58
import com.github.ajalt.clikt.core.subcommands
@@ -11,6 +14,7 @@ import com.github.ajalt.clikt.testing.TestCommand
1114
import com.github.ajalt.clikt.testing.parse
1215
import io.kotest.assertions.throwables.shouldThrow
1316
import io.kotest.matchers.string.shouldContain
17+
import kotlinx.coroutines.test.runTest
1418
import kotlin.js.JsName
1519
import kotlin.test.Test
1620
import kotlin.test.assertEquals
@@ -130,4 +134,34 @@ abstract class CompletionTestBase(private val shell: String) {
130134
message shouldContain shell
131135
message shouldContain "foo"
132136
}
137+
138+
@Test
139+
@JsName("suspending_completion_command")
140+
fun `suspending completion command`() = runTest {
141+
class Foo : SuspendingNoOpCliktCommand()
142+
143+
val message = shouldThrow<PrintCompletionMessage> {
144+
Foo()
145+
.subcommands(SuspendingCompletionCommand(), Foo())
146+
.parse(listOf("generate-completion", shell))
147+
}.message
148+
message shouldContain shell
149+
message shouldContain "foo"
150+
}
151+
152+
@Test
153+
@JsName("chained_completion_command")
154+
fun `chained completion command`() = runTest {
155+
class Foo : ChainedCliktCommand<Unit>() {
156+
override fun run(value: Unit) = Unit
157+
}
158+
159+
val message = shouldThrow<PrintCompletionMessage> {
160+
Foo()
161+
.subcommands(ChainedCompletionCommand(), Foo())
162+
.parse(listOf("generate-completion", shell), Unit)
163+
}.message
164+
message shouldContain shell
165+
message shouldContain "foo"
166+
}
133167
}

0 commit comments

Comments
 (0)