diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5dca3ec7c..0098c14be 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,9 +33,8 @@ jobs: with: distribution: 'zulu' java-version: 17 - - uses: gradle/actions/setup-gradle@v3 - with: - arguments: ${{matrix.TEST_TASK}} --stacktrace + - uses: gradle/actions/setup-gradle@v4 + - run: ./gradlew ${{matrix.TEST_TASK}} --stacktrace - name: Upload the build report if: failure() uses: actions/upload-artifact@master @@ -53,10 +52,9 @@ jobs: distribution: 'zulu' java-version: 17 - name: Deploy to sonatype - uses: gradle/actions/setup-gradle@v3 - with: - # disable configuration cache due to https://github.com/gradle/gradle/issues/22779 - arguments: publishToMavenCentral -PsnapshotVersion=true --no-configuration-cache + uses: gradle/actions/setup-gradle@v4 + # disable configuration cache due to https://github.com/gradle/gradle/issues/22779 + - run: ./gradlew publishToMavenCentral -PsnapshotVersion=true --no-configuration-cache env: ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.ORG_GRADLE_PROJECT_mavenCentralPassword }} ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.ORG_GRADLE_PROJECT_signingInMemoryKey }} diff --git a/.github/workflows/gradle-wrapper-validation.yml b/.github/workflows/gradle-wrapper-validation.yml deleted file mode 100644 index d58f09f62..000000000 --- a/.github/workflows/gradle-wrapper-validation.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: "Validate Gradle Wrapper" -on: - pull_request: - paths: - - 'gradlew' - - 'gradlew.bat' - - 'gradle/wrapper/' -jobs: - validation: - name: "Validation" - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: gradle/actions/wrapper-validation@v3 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d6b5d8a0f..8fc40df38 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: with: distribution: 'zulu' java-version: 17 - - uses: gradle/actions/setup-gradle@v3 + - uses: gradle/actions/setup-gradle@v4 with: # disable configuration cache due to https://github.com/gradle/gradle/issues/22779 arguments: publishToMavenCentral --no-configuration-cache diff --git a/CHANGELOG.md b/CHANGELOG.md index ec07c7b2c..a88f1b5f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Added `associate{}`, `associateBy{}`, and `associateWith{}` transforms for options that allow you to convert the keys and values of the map. ([#529](https://github.com/ajalt/clikt/pull/529)) - Added support for aliasing options to other options. ([#535](https://github.com/ajalt/clikt/pull/535)) - Added `limit` and `ignoreCase` parameters to `option().split()`. ([#541](https://github.com/ajalt/clikt/pull/541)) +- Support calling `--help` on subcommands when parents have required parameters. ### Changed - In a subcommand with and an `argument()` with `multiple()` or `optional()`, the behavior is now the same regardless of the value of `allowMultipleSubcommands`: if a token matches a subcommand name, it's now treated as a subcommand rather than a positional argument. @@ -39,8 +40,8 @@ - **Breaking Change:** `CliktCommand.main` and `CliktCommand.parse` are now extension functions rather than methods. - **Breaking Change:** `Context.obj` and `Context.terminal`, and `OptionTransformContext.terminal` are now extension functions rather than properties. - **Breaking Change:** The `RenderedSection` and `DefinitionRow` classes have moved to `AbstractHelpFormatter`. +- Markdown support in the help formatter is no longer included by default. To enable it, include the `:clikt-markdown` dependency and call `yourCommand.installMordantMarkdown()` before parsing. - Updated Kotlin to 2.0.0 -- Support calling `--help` on subcommands when parents have required parameters. ### Fixed - Fixed excess arguments not being reported when `allowMultipleSubcommands=true` and a subcommand has excess arguments followed by another subcommand. diff --git a/clikt-mordant-markdown/README.md b/clikt-mordant-markdown/README.md new file mode 100755 index 000000000..be72be609 --- /dev/null +++ b/clikt-mordant-markdown/README.md @@ -0,0 +1,11 @@ +# Module clikt-mordant-markdown + +This module provides the `MordantMarkdownHelpFormatter` that renders text as markdown. + +## Installation + +```kotlin +dependencies { + implementation("com.github.ajalt.clikt:clikt-markdown:$cliktVersion") +} +``` diff --git a/clikt-mordant-markdown/api/clikt-mordant-markdown.api b/clikt-mordant-markdown/api/clikt-mordant-markdown.api new file mode 100755 index 000000000..8a04046a8 --- /dev/null +++ b/clikt-mordant-markdown/api/clikt-mordant-markdown.api @@ -0,0 +1,10 @@ +public final class com/github/ajalt/clikt/core/MordantMarkdownContextKt { + public static final fun installMordantMarkdown (Lcom/github/ajalt/clikt/core/BaseCliktCommand;)V +} + +public class com/github/ajalt/clikt/output/MordantMarkdownHelpFormatter : com/github/ajalt/clikt/output/MordantHelpFormatter { + public fun (Lcom/github/ajalt/clikt/core/Context;Ljava/lang/String;ZZ)V + public synthetic fun (Lcom/github/ajalt/clikt/core/Context;Ljava/lang/String;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun renderWrappedText (Ljava/lang/String;)Lcom/github/ajalt/mordant/rendering/Widget; +} + diff --git a/clikt-mordant-markdown/build.gradle.kts b/clikt-mordant-markdown/build.gradle.kts new file mode 100644 index 000000000..c87228293 --- /dev/null +++ b/clikt-mordant-markdown/build.gradle.kts @@ -0,0 +1,50 @@ +import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl + + +plugins { + kotlin("multiplatform") +} + +kotlin { + jvm() + + js { nodejs() } + @OptIn(ExperimentalWasmDsl::class) + wasmJs { nodejs() } + + linuxX64() + linuxArm64() + mingwX64() + macosX64() + macosArm64() + + iosArm64() + iosX64() + + sourceSets { + val commonMain by getting { + dependencies { + api(project(":clikt")) + api(project(":clikt-mordant")) + api(libs.mordant) + api(libs.mordant.markdown) + } + } + + val commonTest by getting { + dependencies { + api(kotlin("test")) + api(libs.kotest) + api(libs.coroutines.core) + api(libs.coroutines.test) + } + } + + val jvmTest by getting { + dependencies { + api(libs.systemrules) + api(libs.jimfs) + } + } + } +} diff --git a/clikt-mordant-markdown/gradle.properties b/clikt-mordant-markdown/gradle.properties new file mode 100755 index 000000000..b804bd606 --- /dev/null +++ b/clikt-mordant-markdown/gradle.properties @@ -0,0 +1,2 @@ +POM_ARTIFACT_ID=clikt-markdown +POM_NAME=Clikt Markdown Support diff --git a/clikt-mordant-markdown/src/commonMain/kotlin/com/github/ajalt/clikt/core/MordantMarkdownContext.kt b/clikt-mordant-markdown/src/commonMain/kotlin/com/github/ajalt/clikt/core/MordantMarkdownContext.kt new file mode 100644 index 000000000..d07324a29 --- /dev/null +++ b/clikt-mordant-markdown/src/commonMain/kotlin/com/github/ajalt/clikt/core/MordantMarkdownContext.kt @@ -0,0 +1,14 @@ +package com.github.ajalt.clikt.core + +import com.github.ajalt.clikt.output.MordantMarkdownHelpFormatter + +/** + * Set up this command's context to use Mordant for rendering output as Markdown. + */ +fun BaseCliktCommand<*>.installMordantMarkdown() { + installMordant(force = true) + configureContext { + helpFormatter = { MordantMarkdownHelpFormatter(it) } + } +} + diff --git a/clikt-mordant-markdown/src/commonMain/kotlin/com/github/ajalt/clikt/output/MordantMarkdownHelpFormatter.kt b/clikt-mordant-markdown/src/commonMain/kotlin/com/github/ajalt/clikt/output/MordantMarkdownHelpFormatter.kt new file mode 100644 index 000000000..4e71e598d --- /dev/null +++ b/clikt-mordant-markdown/src/commonMain/kotlin/com/github/ajalt/clikt/output/MordantMarkdownHelpFormatter.kt @@ -0,0 +1,47 @@ +package com.github.ajalt.clikt.output + +import com.github.ajalt.clikt.core.Context +import com.github.ajalt.clikt.core.UsageError +import com.github.ajalt.clikt.core.terminal +import com.github.ajalt.clikt.output.HelpFormatter.ParameterHelp +import com.github.ajalt.mordant.markdown.Markdown +import com.github.ajalt.mordant.rendering.Theme +import com.github.ajalt.mordant.rendering.Whitespace +import com.github.ajalt.mordant.rendering.Widget +import com.github.ajalt.mordant.table.verticalLayout +import com.github.ajalt.mordant.widgets.Text +import com.github.ajalt.mordant.widgets.definitionList +import com.github.ajalt.mordant.widgets.withPadding + +/** + * A [HelpFormatter] that uses Mordant to render its output as GitHub Flavored Markdown. + * + * To customize help text, you can create a subclass and set it as the `helpFormatter` on your + * command's context. + */ +open class MordantMarkdownHelpFormatter( + /** + * The current command's context. + */ + context: Context, + /** + * The string to show before the names of required options, or null to not show a mark. + */ + requiredOptionMarker: String? = null, + /** + * If true, the default values will be shown in the help text for parameters that have them. + */ + showDefaultValues: Boolean = false, + /** + * If true, a tag indicating the parameter is required will be shown after the description of + * required parameters. + */ + showRequiredTag: Boolean = false, +) : MordantHelpFormatter( + context, + requiredOptionMarker, + showDefaultValues, + showRequiredTag +) { + override fun renderWrappedText(text: String): Widget = Markdown(text, showHtml = true) +} diff --git a/clikt-mordant/api/clikt-mordant.api b/clikt-mordant/api/clikt-mordant.api old mode 100644 new mode 100755 index c5a76f4a3..c5396ea3e --- a/clikt-mordant/api/clikt-mordant.api +++ b/clikt-mordant/api/clikt-mordant.api @@ -39,7 +39,8 @@ public final class com/github/ajalt/clikt/core/MordantContextKt { public static final fun getTerminal (Lcom/github/ajalt/clikt/core/Context$Builder;)Lcom/github/ajalt/mordant/terminal/Terminal; public static final fun getTerminal (Lcom/github/ajalt/clikt/core/Context;)Lcom/github/ajalt/mordant/terminal/Terminal; public static final fun getTheme (Lcom/github/ajalt/clikt/core/Context;)Lcom/github/ajalt/mordant/rendering/Theme; - public static final fun installMordant (Lcom/github/ajalt/clikt/core/BaseCliktCommand;)V + public static final fun installMordant (Lcom/github/ajalt/clikt/core/BaseCliktCommand;Z)V + public static synthetic fun installMordant$default (Lcom/github/ajalt/clikt/core/BaseCliktCommand;ZILjava/lang/Object;)V public static final fun setTerminal (Lcom/github/ajalt/clikt/core/Context$Builder;Lcom/github/ajalt/mordant/terminal/Terminal;)V } @@ -74,6 +75,7 @@ public class com/github/ajalt/clikt/output/MordantHelpFormatter : com/github/aja public synthetic fun renderProlog (Ljava/lang/String;)Ljava/lang/Object; protected fun renderUsage (Ljava/util/List;Ljava/lang/String;)Lcom/github/ajalt/mordant/rendering/Widget; public synthetic fun renderUsage (Ljava/util/List;Ljava/lang/String;)Ljava/lang/Object; + public fun renderWrappedText (Ljava/lang/String;)Lcom/github/ajalt/mordant/rendering/Widget; protected fun styleArgumentName (Ljava/lang/String;)Ljava/lang/String; protected fun styleError (Ljava/lang/String;)Ljava/lang/String; protected fun styleHelpTag (Ljava/lang/String;)Ljava/lang/String; diff --git a/clikt-mordant/build.gradle.kts b/clikt-mordant/build.gradle.kts index bb25a8abc..8a5cd3ebf 100644 --- a/clikt-mordant/build.gradle.kts +++ b/clikt-mordant/build.gradle.kts @@ -9,45 +9,33 @@ plugins { kotlin { jvm() - js { - nodejs() - browser() - } + + js { nodejs() } @OptIn(ExperimentalWasmDsl::class) - wasmJs { - nodejs() - } + wasmJs { nodejs() } linuxX64() linuxArm64() mingwX64() macosX64() macosArm64() + iosArm64() iosX64() + iosSimulatorArm64() + watchosX64() + watchosArm32() + watchosArm64() + watchosSimulatorArm64() + tvosX64() + tvosArm64() + tvosSimulatorArm64() sourceSets { val commonMain by getting { dependencies { api(project(":clikt")) api(libs.mordant) - api(libs.mordant.markdown) - } - } - - val commonTest by getting { - dependencies { - api(kotlin("test")) - api(libs.kotest) - api(libs.coroutines.core) - api(libs.coroutines.test) - } - } - - val jvmTest by getting { - dependencies { - api(libs.systemrules) - api(libs.jimfs) } } } diff --git a/clikt-mordant/src/commonMain/kotlin/com/github/ajalt/clikt/core/MordantContext.kt b/clikt-mordant/src/commonMain/kotlin/com/github/ajalt/clikt/core/MordantContext.kt index e1dcb3a5b..7a582c2e9 100644 --- a/clikt-mordant/src/commonMain/kotlin/com/github/ajalt/clikt/core/MordantContext.kt +++ b/clikt-mordant/src/commonMain/kotlin/com/github/ajalt/clikt/core/MordantContext.kt @@ -44,12 +44,15 @@ val BaseCliktCommand<*>.terminal: Terminal * * This is done automatically for [CliktCommand]s, but you can call this if you are making a custom * command class. + * + * @param force If true, install mordant even if the parent command has already installed a help + * formatter. */ -fun BaseCliktCommand<*>.installMordant() { +fun BaseCliktCommand<*>.installMordant(force: Boolean = false) { configureContext { // Only install mordant if we're the parent command so that we don't override inherited // settings. - if (parent != null) return@configureContext + if (!force && parent != null) return@configureContext helpFormatter = { MordantHelpFormatter(it) } readEnvvar = { MultiplatformSystem.readEnvironmentVariable(it) } readArgumentFile = { MultiplatformSystem.readFileAsUtf8(it) ?: throw FileNotFound(it) } diff --git a/clikt-mordant/src/commonMain/kotlin/com/github/ajalt/clikt/output/MordantHelpFormatter.kt b/clikt-mordant/src/commonMain/kotlin/com/github/ajalt/clikt/output/MordantHelpFormatter.kt index b2bdcfe4b..6b0f157fb 100644 --- a/clikt-mordant/src/commonMain/kotlin/com/github/ajalt/clikt/output/MordantHelpFormatter.kt +++ b/clikt-mordant/src/commonMain/kotlin/com/github/ajalt/clikt/output/MordantHelpFormatter.kt @@ -4,7 +4,6 @@ import com.github.ajalt.clikt.core.Context import com.github.ajalt.clikt.core.UsageError import com.github.ajalt.clikt.core.terminal import com.github.ajalt.clikt.output.HelpFormatter.ParameterHelp -import com.github.ajalt.mordant.markdown.Markdown import com.github.ajalt.mordant.rendering.Theme import com.github.ajalt.mordant.rendering.Whitespace import com.github.ajalt.mordant.rendering.Widget @@ -83,11 +82,11 @@ open class MordantHelpFormatter( } override fun renderProlog(prolog: String): Widget { - return Markdown(prolog, showHtml = true).withPadding(padEmptyLines = false) { left = 2 } + return renderWrappedText(prolog).withPadding(padEmptyLines = false) { left = 2 } } override fun renderEpilog(epilog: String): Widget { - return Markdown(epilog, showHtml = true) + return renderWrappedText(epilog) } override fun renderParameters( @@ -97,13 +96,14 @@ open class MordantHelpFormatter( entry(section.title, section.content) } } + override fun renderOptionGroup( help: String?, parameters: List, ): Widget { val options = parameters.map(::renderOptionDefinition) if (help == null) return buildParameterList(options) - val markdown = Markdown(help, showHtml = true).withPadding(padEmptyLines = false) { + val markdown = renderWrappedText(help).withPadding(padEmptyLines = false) { top = 1 left = 2 bottom = 1 @@ -141,7 +141,7 @@ open class MordantHelpFormatter( override fun renderDefinitionDescription(row: DefinitionRow): Widget { return if (row.description.isBlank()) Text("") - else (Markdown(row.description, showHtml = true)) + else renderWrappedText(row.description) } override fun buildParameterList(rows: List): Widget { @@ -152,4 +152,10 @@ open class MordantHelpFormatter( } } } + + open fun renderWrappedText(text: String): Widget { + // Replace double newlines with a hard line break since there's no Whitespace equivalent for + // markdown's paragraph behavior. + return Text(text.replace("\n\n", "\u0085\u0085"), whitespace = Whitespace.NORMAL) + } } diff --git a/clikt/build.gradle.kts b/clikt/build.gradle.kts index fc93f62bd..b7cc3c930 100644 --- a/clikt/build.gradle.kts +++ b/clikt/build.gradle.kts @@ -11,14 +11,12 @@ plugins { kotlin { jvm() - js { - nodejs() - browser() - } + + js { nodejs() } @OptIn(ExperimentalWasmDsl::class) - wasmJs { - nodejs() - } + wasmJs { nodejs() } + @OptIn(ExperimentalWasmDsl::class) + wasmWasi { nodejs() } linuxX64() linuxArm64() @@ -26,7 +24,6 @@ kotlin { macosX64() macosArm64() - // these targets are only supported in the core module iosArm64() iosX64() iosSimulatorArm64() @@ -37,10 +34,6 @@ kotlin { tvosX64() tvosArm64() tvosSimulatorArm64() - @OptIn(ExperimentalWasmDsl::class) - wasmWasi { - nodejs() - } } // https://youtrack.jetbrains.com/issue/KT-63014 diff --git a/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/output/PlaintextHelpFormatter.kt b/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/output/PlaintextHelpFormatter.kt index 068501142..88f024480 100644 --- a/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/output/PlaintextHelpFormatter.kt +++ b/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/output/PlaintextHelpFormatter.kt @@ -75,6 +75,7 @@ class PlaintextHelpFormatter( parameters: List, ): String = buildString { if (help != null) { + appendLine() appendLine(help) appendLine() } diff --git a/docs/advanced.md b/docs/advanced.md index 77df2e5b5..4df3777a5 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -565,7 +565,7 @@ dependencies { ``` The [CliktCommand] class is only available in the full module, so you'll need to use -[CoreCliktCommand] instead. The `CoreCliktCommand` has the same API as `CliktCommand`, but it +[CoreCliktCommand] (or [CoreNoOpCliktCommand]) instead. The `CoreCliktCommand` has the same API as `CliktCommand`, but it doesn't have any of these features built in: - Text wrapping, formatting, markdown, or color support @@ -640,7 +640,8 @@ These platforms are supported for the [core module](#core-module) only. [BaseCliktCommand]: api/clikt/com.github.ajalt.clikt.core/-base-clikt-command/index.html [CliktCommand]: api/clikt-mordant/com.github.ajalt.clikt.core/-clikt-command/index.html -[CliktCommand]: api/clikt/com.github.ajalt.clikt.core/-core-clikt-command/index.html +[CoreCliktCommand]: api/clikt/com.github.ajalt.clikt.core/-core-clikt-command/index.html +[CoreNoOpCliktCommand]: api/clikt/com.github.ajalt.clikt.core/-core-no-op-clikt-command/index.html [CommandLineParser]: api/clikt/com.github.ajalt.clikt.parsers/-command-line-parser/index.html [CommandLineParser.main]: api/clikt/com.github.ajalt.clikt.parsers/-command-line-parser/main.html [CommandLineParser.parse]: api/clikt/com.github.ajalt.clikt.parsers/-command-line-parser/parse.html diff --git a/docs/documenting.md b/docs/documenting.md index c09570721..2627d20c2 100644 --- a/docs/documenting.md +++ b/docs/documenting.md @@ -67,12 +67,31 @@ describe arguments in the command help. ## Markdown in help texts -All help texts use Mordant to render Markdown. You can use all the normal markdown features, such as -lists, tables, and even hyperlinks if your terminal supports them. +You can configure Clikt to use Mordant to render Markdown in help texts. You can use all the normal +markdown features, such as lists, tables, and even hyperlinks if your terminal supports them. + +First, add the `:clitk-markdown` dependency to your project: + +```kotlin +dependencies { + implementation("com.github.ajalt.clikt:clikt-markdown:$cliktVersion") +} +``` + +And install the markdown help formatter on your command: + +```kotlin +val command = MyCommand().installMordantMarkdown() +``` + +Then you can use markdown in your help strings: === "Example" ```kotlin class Tool : NoOpCliktCommand() { + init { + installMordantMarkdown() + } val option by option().help { """ | This | is | a | table | diff --git a/settings.gradle.kts b/settings.gradle.kts index c0e0c4e27..4e77ae234 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,7 @@ include("clikt") include("clikt-mordant") +include("clikt-mordant-markdown") +include("test") include("samples:copy") include("samples:repo") include("samples:validation") diff --git a/test/README.md b/test/README.md new file mode 100755 index 000000000..9f8f877b0 --- /dev/null +++ b/test/README.md @@ -0,0 +1,3 @@ +# Module test + +This module contains all the tests for the project. It depends on all other modules. diff --git a/test/build.gradle.kts b/test/build.gradle.kts new file mode 100644 index 000000000..0b62eabf9 --- /dev/null +++ b/test/build.gradle.kts @@ -0,0 +1,59 @@ +import org.jetbrains.dokka.gradle.DokkaTaskPartial +import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl + + +plugins { + kotlin("multiplatform") + alias(libs.plugins.publish) +} + +kotlin { + jvm() + + js { nodejs() } + @OptIn(ExperimentalWasmDsl::class) + wasmJs { nodejs() } + + linuxX64() + linuxArm64() + mingwX64() + macosX64() + macosArm64() + + iosArm64() + iosX64() + + sourceSets { + val commonMain by getting { + dependencies { + api(project(":clikt")) + api(project(":clikt-mordant")) + api(project(":clikt-mordant-markdown")) + api(libs.mordant) + api(libs.mordant.markdown) + } + } + + val commonTest by getting { + dependencies { + api(kotlin("test")) + api(libs.kotest) + api(libs.coroutines.core) + api(libs.coroutines.test) + } + } + + val jvmTest by getting { + dependencies { + api(libs.systemrules) + api(libs.jimfs) + } + } + } +} + +tasks.withType { + dokkaSourceSets.configureEach { + includes.from("README.md") + } +} diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/command/ChainedCliktCommandTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/command/ChainedCliktCommandTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/command/ChainedCliktCommandTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/command/ChainedCliktCommandTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/command/SuspendingCliktCommandTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/command/SuspendingCliktCommandTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/command/SuspendingCliktCommandTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/command/SuspendingCliktCommandTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/completion/BashCompletionTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/BashCompletionTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/completion/BashCompletionTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/BashCompletionTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/completion/CompletionTestBase.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/CompletionTestBase.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/completion/CompletionTestBase.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/CompletionTestBase.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/completion/EnvvarCompletionTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/EnvvarCompletionTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/completion/EnvvarCompletionTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/EnvvarCompletionTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/completion/FishCompletionTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/FishCompletionTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/completion/FishCompletionTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/completion/FishCompletionTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/core/CliktCommandTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/core/CliktCommandTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/core/CliktCommandTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/core/CliktCommandTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/core/ContextTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/core/ContextTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/core/ContextTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/core/ContextTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/core/ExceptionsTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/core/ExceptionsTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/core/ExceptionsTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/core/ExceptionsTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/output/MordantHelpFormatterTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/output/MordantHelpFormatterTest.kt similarity index 95% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/output/MordantHelpFormatterTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/output/MordantHelpFormatterTest.kt index 796e9ea3e..293f1a967 100644 --- a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/output/MordantHelpFormatterTest.kt +++ b/test/src/commonTest/kotlin/com/github/ajalt/clikt/output/MordantHelpFormatterTest.kt @@ -16,6 +16,7 @@ import com.github.ajalt.mordant.terminal.Terminal import io.kotest.data.blocking.forAll import io.kotest.data.row import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf import kotlin.js.JsName import kotlin.test.Test @@ -29,10 +30,13 @@ class MordantHelpFormatterTest { command: CliktCommand = c, helpNames: Set = emptySet(), ) { - command.context { + val formattedHelp = command.context { terminal = Terminal(width = width, ansiLevel = AnsiLevel.NONE) helpOptionNames = helpNames - }.getFormattedHelp() shouldBe expected.trimMargin() + }.getFormattedHelp() + command.currentContext.helpFormatter(command.currentContext) + .shouldBeInstanceOf() + formattedHelp shouldBe expected.trimMargin() } @Test @@ -222,39 +226,6 @@ class MordantHelpFormatterTest { ) } - @Test - @JsName("help_output_prolog_list") - fun `prolog list`() { - val c = TestCommand( - name = "prog", - help = """ - |Lorem ipsum dolor sit amet, consectetur adipiscing elit. - | - |- Morbi id libero purus. - |- Praesent sit amet neque tellus. - | - |Vivamus dictum varius massa, at euismod turpis maximus eu. Suspendisse molestie mauris at - |turpis bibendum egestas. - """.trimMargin(), - ) - doTest( - """ - |Usage: prog - | - | Lorem ipsum dolor sit amet, consectetur adipiscing - | elit. - | - | • Morbi id libero purus. - | • Praesent sit amet neque tellus. - | - | Vivamus dictum varius massa, at euismod turpis - | maximus eu. Suspendisse molestie mauris at turpis - | bibendum egestas. - """, - width = 54, - command = c - ) - } @Test @JsName("help_output_one_opt_manual_line_break_narrow") diff --git a/test/src/commonTest/kotlin/com/github/ajalt/clikt/output/MordantMarkdownHelpFormatterTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/output/MordantMarkdownHelpFormatterTest.kt new file mode 100644 index 000000000..ccca6adcf --- /dev/null +++ b/test/src/commonTest/kotlin/com/github/ajalt/clikt/output/MordantMarkdownHelpFormatterTest.kt @@ -0,0 +1,251 @@ +package com.github.ajalt.clikt.output + +import com.github.ajalt.clikt.core.* +import com.github.ajalt.clikt.parameters.arguments.* +import com.github.ajalt.clikt.parameters.groups.* +import com.github.ajalt.clikt.parameters.options.* +import com.github.ajalt.clikt.parameters.transform.theme +import com.github.ajalt.clikt.parameters.types.choice +import com.github.ajalt.clikt.parameters.types.int +import com.github.ajalt.clikt.testing.TestCommand +import com.github.ajalt.clikt.testing.test +import com.github.ajalt.mordant.rendering.AnsiLevel +import com.github.ajalt.mordant.rendering.TextColors.* +import com.github.ajalt.mordant.rendering.Theme +import com.github.ajalt.mordant.terminal.Terminal +import io.kotest.data.blocking.forAll +import io.kotest.data.row +import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf +import kotlin.js.JsName +import kotlin.test.Test + + +@Suppress("unused") +class MordantMarkdownHelpFormatterTest { + private val c = TestCommand(name = "prog") + private fun doTest( + expected: String, + width: Int = 79, + command: CliktCommand = c, + helpNames: Set = emptySet(), + ) { + command.installMordantMarkdown() + val formattedHelp = command.context { + terminal = Terminal(width = width, ansiLevel = AnsiLevel.NONE) + helpOptionNames = helpNames + }.getFormattedHelp() + command.currentContext.helpFormatter(command.currentContext) + .shouldBeInstanceOf() + formattedHelp shouldBe expected.trimMargin() + } + + @Test + @JsName("help_output_one_opt_prolog_multi_paragraph") + fun `one opt prolog multi paragraph`() { + val c = TestCommand( + name = "prog", + help = """ + |Lorem ipsum dolor sit amet, consectetur adipiscing elit. + | + |Vivamus dictum varius massa, at euismod turpis maximus eu. Suspendisse molestie mauris at + |turpis bibendum egestas. + | + |Morbi id libero purus. Praesent sit amet neque tellus. Vestibulum in condimentum turpis, in + |consectetur ex. + """.trimMargin(), + ) + c.registerOption(c.option("--aa", "-a", help = "some thing to live by").int()) + doTest( + """ + |Usage: prog [] + | + | Lorem ipsum dolor sit amet, consectetur adipiscing + | elit. + | + | Vivamus dictum varius massa, at euismod turpis + | maximus eu. Suspendisse molestie mauris at turpis + | bibendum egestas. + | + | Morbi id libero purus. Praesent sit amet neque + | tellus. Vestibulum in condimentum turpis, in + | consectetur ex. + | + |Options: + | -a, --aa= some thing to live by + """, + width = 54, + command = c + ) + } + + @Test + @JsName("help_output_prolog_list") + fun `prolog list`() { + val c = TestCommand( + name = "prog", + help = """ + |Lorem ipsum dolor sit amet, consectetur adipiscing elit. + | + |- Morbi id libero purus. + |- Praesent sit amet neque tellus. + | + |Vivamus dictum varius massa, at euismod turpis maximus eu. Suspendisse molestie mauris at + |turpis bibendum egestas. + """.trimMargin(), + ) + doTest( + """ + |Usage: prog + | + | Lorem ipsum dolor sit amet, consectetur adipiscing + | elit. + | + | • Morbi id libero purus. + | • Praesent sit amet neque tellus. + | + | Vivamus dictum varius massa, at euismod turpis + | maximus eu. Suspendisse molestie mauris at turpis + | bibendum egestas. + """, + width = 54, + command = c + ) + } + + @Test + @JsName("help_output_one_opt_manual_line_break_narrow") + fun `one opt manual line break narrow`() { + c.registerOption( + c.option( + "--aa", + "-a", + help = "Lorem ipsum dolor\u0085(sit amet, consectetur)" + ) + ) + doTest( + """ + |Usage: prog [] + | + |Options: + | -a, --aa= + | Lorem ipsum dolor + | (sit amet, consectetur) + """, + width = 35 + ) + } + + @Test + @JsName("help_output_one_opt_manual_line_break_wide") + fun `one opt manual line break wide`() { + c.registerOption( + c.option( + "--aa", + "-a", + help = "Lorem ipsum dolor\u0085(sit amet, consectetur)" + ) + ) + doTest( + """ + |Usage: prog [] + | + |Options: + | -a, --aa= Lorem ipsum dolor + | (sit amet, consectetur) + """ + ) + } + + @Test + @JsName("help_output_option_wrapping") + fun `option wrapping`() { + c.registerOption( + c.option("-x", metavar = "X", help = "one very very very very very very long option") + .pair() + ) + c.registerOption( + c.option( + "-y", + "--yy", + metavar = "Y", + help = "a shorter but still long option" + ) + ) + c.registerOption( + c.option( + "-z", + "--zzzzzzzzzzzzz", + metavar = "ZZZZZZZZ", + help = "a short option" + ) + ) + c.registerOption( + c.option( + "-t", "--entirely-too-long-option", metavar = "WOWSOLONG", + help = "this option has a long name and a long description" + ) + ) + doTest( + """ + |Usage: prog [] + | + |Options: + | -x=... one very very very very very very long + | option + | -y, --yy= a shorter but still long option + | -z, --zzzzzzzzzzzzz= + | a short option + | -t, --entirely-too-long-option= + | this option has a long name and a long + | description + """, + width = 54 + ) + } + + @Test + @JsName("help_output_option_groups") + fun `option groups`() { + class G : OptionGroup("Grouped") { + override val groupHelp: String = + "This is the help text for the option group named Grouped. " + + "This text should wrap onto exactly three lines." + val a by option("--aa", "-a", help = "some thing to live by aa") + val c by option("--cc", "-c", help = "some thing to live by cc") + } + + class G2 : OptionGroup("Singleton") { + val b by option("--bb", "-b", help = "some thing to live by bb") + } + + class C : TestCommand(name = "prog") { + val g by G() + val g2 by G2() + val o by option("--dd", "-d", help = "some thing to live by dd") + } + + doTest( + """ + |Usage: prog [] + | + |Grouped: + | + | This is the help text for the option group named + | Grouped. This text should wrap onto exactly three + | lines. + | + | -a, --aa= some thing to live by aa + | -c, --cc= some thing to live by cc + | + |Singleton: + | -b, --bb= some thing to live by bb + | + |Options: + | -d, --dd= some thing to live by dd + """, + width = 54, + command = C() + ) + } +} diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/output/PlaintextHelpFormatterTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/output/PlaintextHelpFormatterTest.kt similarity index 86% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/output/PlaintextHelpFormatterTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/output/PlaintextHelpFormatterTest.kt index 8aba9e785..971679ef4 100644 --- a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/output/PlaintextHelpFormatterTest.kt +++ b/test/src/commonTest/kotlin/com/github/ajalt/clikt/output/PlaintextHelpFormatterTest.kt @@ -1,8 +1,6 @@ package com.github.ajalt.clikt.output -import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.core.context -import com.github.ajalt.clikt.core.subcommands +import com.github.ajalt.clikt.core.* import com.github.ajalt.clikt.parameters.arguments.Argument import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.arguments.multiple @@ -11,28 +9,42 @@ import com.github.ajalt.clikt.parameters.groups.* import com.github.ajalt.clikt.parameters.options.* import com.github.ajalt.clikt.parameters.types.choice import com.github.ajalt.clikt.parameters.types.int -import com.github.ajalt.clikt.testing.TestCommand -import com.github.ajalt.clikt.testing.test +import io.kotest.assertions.throwables.shouldThrow import io.kotest.data.blocking.forAll import io.kotest.data.row import io.kotest.matchers.shouldBe +import io.kotest.matchers.types.shouldBeInstanceOf +import io.kotest.matchers.types.shouldNotBeInstanceOf import kotlin.js.JsName import kotlin.test.Test class PlaintextHelpFormatterTest { - private val c = TestCommand(name = "prog").context { - helpFormatter = { PlaintextHelpFormatter(it) } + class TestCoreCommand( + name: String, + private val help: String = "", + private val epilog: String = "", + override val hiddenFromHelp: Boolean = false, + override val helpTags: Map = emptyMap() + ) : CoreNoOpCliktCommand(name) { + override fun help(context: Context): String = help + override fun helpEpilog(context: Context): String = epilog } + private val c = CoreNoOpCliktCommand(name = "prog") + private fun doTest( expected: String, - command: CliktCommand = c, + command: CoreCliktCommand = c, helpNames: Set = emptySet(), ) { - command.context { + command.shouldNotBeInstanceOf() + val help = command.context { helpOptionNames = helpNames - }.getFormattedHelp() shouldBe expected.trimMargin() + }.getFormattedHelp() + command.currentContext.helpFormatter(command.currentContext) + .shouldBeInstanceOf() + help shouldBe expected.trimMargin() } @Test @@ -52,18 +64,22 @@ class PlaintextHelpFormatterTest { "Usage: prog [] ... []" ), row( - listOf(c.option("-x"), c.argument("FOO").optional(), TestCommand(name = "bar")), + listOf( + c.option("-x"), + c.argument("FOO").optional(), + CoreNoOpCliktCommand(name = "bar") + ), "Usage: prog [] [] []..." ) ) { params, expected -> - val c = TestCommand(name = "prog").context { + val c = CoreNoOpCliktCommand(name = "prog").context { helpOptionNames = emptySet() } for (p in params) { when (p) { is Argument -> c.registerArgument(p) is Option -> c.registerOption(p) - is CliktCommand -> c.subcommands(p) + is CoreCliktCommand -> c.subcommands(p) else -> error("Unknown param type: $p") } } @@ -135,7 +151,7 @@ class PlaintextHelpFormatterTest { @Test @JsName("help_output_one_opt_prolog") fun `one opt prolog`() { - val c = TestCommand( + val c = TestCoreCommand( name = "prog", help = "Lorem Ipsum.", epilog = "Dolor Sit Amet." @@ -172,7 +188,7 @@ class PlaintextHelpFormatterTest { val b by option("--bb", "-b", help = "some thing to live by bb") } - class C : TestCommand(name = "prog") { + class C : CoreNoOpCliktCommand(name = "prog") { val g by G() val g2 by G2() val o by option("--dd", "-d", help = "some thing to live by dd") @@ -184,8 +200,7 @@ class PlaintextHelpFormatterTest { | |Grouped: | - | This is the help text for the option group named Grouped. This text should - | wrap onto exactly three lines. + |This is the help text for the option group named Grouped. This text should wrap onto exactly three lines. | | -a, --aa= some thing to live by aa | -c, --cc= some thing to live by cc @@ -204,7 +219,7 @@ class PlaintextHelpFormatterTest { @Suppress("unused") @JsName("help_output_arguments") fun arguments() { - class C : TestCommand(name = "prog") { + class C : CoreNoOpCliktCommand(name = "prog") { val foo by argument(help = "some thing to live by") val bar by argument(help = "another argument").optional() } @@ -224,10 +239,10 @@ class PlaintextHelpFormatterTest { @JsName("subcommands") fun subcommands() { c.subcommands( - TestCommand(name = "foo", help = "some thing to live by"), - TestCommand(name = "bar", help = "another argument"), - TestCommand(name = "baz"), - TestCommand(name = "qux", hiddenFromHelp = true), + TestCoreCommand(name = "foo", help = "some thing to live by"), + TestCoreCommand(name = "bar", help = "another argument"), + TestCoreCommand(name = "baz"), + TestCoreCommand(name = "qux", hiddenFromHelp = true), ) doTest( """ @@ -273,7 +288,7 @@ class PlaintextHelpFormatterTest { val opt2 by option() } - class C : TestCommand() { + class C : CoreNoOpCliktCommand() { val opt by option(help = "select group").groupChoice("g1" to G1(), "g2" to G2()) } doTest( @@ -305,7 +320,7 @@ class PlaintextHelpFormatterTest { val opt2 by option() } - class C : TestCommand() { + class C : CoreNoOpCliktCommand() { val opt by option(help = "select group").groupSwitch("--g1" to G1(), "--g2" to G2()) } @@ -330,7 +345,7 @@ class PlaintextHelpFormatterTest { @Suppress("unused") @JsName("mutually_exclusive_options") fun `mutually exclusive options`() { - class C : TestCommand(name = "prog") { + class C : CoreNoOpCliktCommand(name = "prog") { val ex by mutuallyExclusiveOptions( option("--ex-foo", help = "exclusive foo"), option("--ex-bar", help = "exclusive bar") @@ -350,7 +365,7 @@ class PlaintextHelpFormatterTest { | |Exclusive: | - | These options are exclusive + |These options are exclusive | | --ex-foo= exclusive foo | --ex-bar= exclusive bar @@ -522,8 +537,12 @@ class PlaintextHelpFormatterTest { @JsName("subcommand_tag") fun `subcommand tag`() { c.subcommands( - TestCommand(name = "sub1", help = "sub 1 help"), - TestCommand(name = "sub2", help = "sub 2 help", helpTags = mapOf("deprecated" to "")), + TestCoreCommand(name = "sub1", help = "sub 1 help"), + TestCoreCommand( + name = "sub2", + help = "sub 2 help", + helpTags = mapOf("deprecated" to "") + ), ) doTest( """ @@ -536,16 +555,16 @@ class PlaintextHelpFormatterTest { ) } - @Test @JsName("multi_error") fun `multi error`() { - c.test("--foo --bar").stderr shouldBe """ + shouldThrow { + c.parse(listOf("--foo", "--bar")) + }.let { c.getFormattedHelp(it) } shouldBe """ |Usage: prog [] | |Error: no such option --foo |Error: no such option --bar - | """.trimMargin() } } diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/ArgumentTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/ArgumentTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/ArgumentTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/ArgumentTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/EagerOptionsTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/EagerOptionsTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/EagerOptionsTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/EagerOptionsTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/EnvvarInferTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/EnvvarInferTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/EnvvarInferTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/EnvvarInferTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/EnvvarOptionsTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/EnvvarOptionsTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/EnvvarOptionsTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/EnvvarOptionsTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/MultiUsageErrorTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/MultiUsageErrorTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/MultiUsageErrorTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/MultiUsageErrorTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/OptionSwitchTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/OptionSwitchTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/OptionSwitchTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/OptionSwitchTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/OptionTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/OptionTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/OptionTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/OptionTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/PromptOptionsTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/PromptOptionsTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/PromptOptionsTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/PromptOptionsTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/SubcommandTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/SubcommandTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/SubcommandTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/SubcommandTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/VarargOptionsTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/VarargOptionsTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/VarargOptionsTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/VarargOptionsTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/groups/OptionGroupsTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/groups/OptionGroupsTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/groups/OptionGroupsTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/groups/OptionGroupsTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/BooleanTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/BooleanTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/BooleanTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/BooleanTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/ChoiceTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/ChoiceTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/ChoiceTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/ChoiceTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/DoubleTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/DoubleTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/DoubleTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/DoubleTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/EnumTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/EnumTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/EnumTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/EnumTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/FloatTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/FloatTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/FloatTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/FloatTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/IntTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/IntTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/IntTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/IntTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/LongTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/LongTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/LongTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/LongTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/RangeTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/RangeTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/RangeTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/RangeTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/UIntTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/UIntTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/UIntTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/UIntTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/ULongTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/ULongTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/ULongTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parameters/types/ULongTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parsers/AtFileTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/parsers/AtFileTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/parsers/AtFileTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/parsers/AtFileTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/sources/ChainedValueSourceTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/sources/ChainedValueSourceTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/sources/ChainedValueSourceTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/sources/ChainedValueSourceTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/sources/MapValueSourceTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/sources/MapValueSourceTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/sources/MapValueSourceTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/sources/MapValueSourceTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/testing/TestCommand.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/testing/TestCommand.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/testing/TestCommand.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/testing/TestCommand.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/testing/TestSource.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/testing/TestSource.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/testing/TestSource.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/testing/TestSource.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/testing/TestingUtilsTest.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/testing/TestingUtilsTest.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/testing/TestingUtilsTest.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/testing/TestingUtilsTest.kt diff --git a/clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/testing/utils.kt b/test/src/commonTest/kotlin/com/github/ajalt/clikt/testing/utils.kt similarity index 100% rename from clikt-mordant/src/commonTest/kotlin/com/github/ajalt/clikt/testing/utils.kt rename to test/src/commonTest/kotlin/com/github/ajalt/clikt/testing/utils.kt diff --git a/clikt-mordant/src/jvmTest/kotlin/com/github/ajalt/clikt/core/ContextJvmTest.kt b/test/src/jvmTest/kotlin/com/github/ajalt/clikt/core/ContextJvmTest.kt similarity index 100% rename from clikt-mordant/src/jvmTest/kotlin/com/github/ajalt/clikt/core/ContextJvmTest.kt rename to test/src/jvmTest/kotlin/com/github/ajalt/clikt/core/ContextJvmTest.kt diff --git a/clikt-mordant/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/FileTest.kt b/test/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/FileTest.kt similarity index 100% rename from clikt-mordant/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/FileTest.kt rename to test/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/FileTest.kt diff --git a/clikt-mordant/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/InputStreamTest.kt b/test/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/InputStreamTest.kt similarity index 100% rename from clikt-mordant/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/InputStreamTest.kt rename to test/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/InputStreamTest.kt diff --git a/clikt-mordant/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/OutputStreamTest.kt b/test/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/OutputStreamTest.kt similarity index 100% rename from clikt-mordant/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/OutputStreamTest.kt rename to test/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/OutputStreamTest.kt diff --git a/clikt-mordant/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/PathTest.kt b/test/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/PathTest.kt similarity index 100% rename from clikt-mordant/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/PathTest.kt rename to test/src/jvmTest/kotlin/com/github/ajalt/clikt/parameters/types/PathTest.kt diff --git a/clikt-mordant/src/jvmTest/kotlin/com/github/ajalt/clikt/sources/PropertiesValueSourceTest.kt b/test/src/jvmTest/kotlin/com/github/ajalt/clikt/sources/PropertiesValueSourceTest.kt similarity index 100% rename from clikt-mordant/src/jvmTest/kotlin/com/github/ajalt/clikt/sources/PropertiesValueSourceTest.kt rename to test/src/jvmTest/kotlin/com/github/ajalt/clikt/sources/PropertiesValueSourceTest.kt