From ca618cb48d62b82a4f31754fff42f7b2f41406ac Mon Sep 17 00:00:00 2001 From: Guilherme Lima Pereira Date: Thu, 31 Mar 2022 16:57:50 -0300 Subject: [PATCH 1/2] Add CliktConsole parameter to TermUi.prompt --- .../commonMain/kotlin/com/github/ajalt/clikt/output/TermUi.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/output/TermUi.kt b/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/output/TermUi.kt index 1489322c7..5deb57cdd 100644 --- a/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/output/TermUi.kt +++ b/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/output/TermUi.kt @@ -152,6 +152,7 @@ object TermUi { * @param confirmationPrompt The text to show the user when [requireConfirmation] is true. * @param promptSuffix A delimiter printed between the [text] and the user's input. * @param showDefault If true, the [default] value will be shown as part of the prompt. + * @param console The console to prompt to * @return the user's input, or null if the stdin is not interactive and EOF was encountered. */ fun prompt( @@ -162,9 +163,10 @@ object TermUi { confirmationPrompt: String = "Repeat for confirmation: ", promptSuffix: String = ": ", showDefault: Boolean = true, + console: CliktConsole = defaultCliktConsole(), ): String? { return prompt(text, default, hideInput, requireConfirmation, - confirmationPrompt, promptSuffix, showDefault) { it } + confirmationPrompt, promptSuffix, showDefault, console) { it } } /** From 325280ae1323a59afd8edc318cfb4b6841b779de Mon Sep 17 00:00:00 2001 From: Guilherme Lima Pereira Date: Fri, 1 Apr 2022 23:55:15 -0300 Subject: [PATCH 2/2] Make TermUi internal to the library and add missing APIs to CliktCommand --- .../github/ajalt/clikt/core/CliktCommand.kt | 42 ++++++++++++++++++- .../com/github/ajalt/clikt/output/TermUi.kt | 2 +- docs/advanced.md | 3 -- docs/utilities.md | 18 ++++---- .../ajalt/clikt/samples/aliases/main.kt | 9 ++-- samples/copy/README.md | 2 +- .../github/ajalt/clikt/samples/copy/main.kt | 3 +- .../ajalt/clikt/samples/plugins/commit.kt | 3 +- .../ajalt/clikt/samples/plugins/setuser.kt | 3 +- .../github/ajalt/clikt/samples/repo/main.kt | 3 +- 10 files changed, 59 insertions(+), 29 deletions(-) diff --git a/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/core/CliktCommand.kt b/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/core/CliktCommand.kt index 308557c42..8cb7ef658 100644 --- a/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/core/CliktCommand.kt +++ b/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/core/CliktCommand.kt @@ -263,8 +263,6 @@ abstract class CliktCommand( * * This is similar to [print] or [println], but converts newlines to the system line separator. * - * This is equivalent to calling [TermUi.echo] with the console from the current context. - * * @param message The message to print. * @param trailingNewline If true, behave like [println], otherwise behave like [print] * @param err If true, print to stderr instead of stdout @@ -278,6 +276,46 @@ abstract class CliktCommand( TermUi.echo(message, trailingNewline, err, currentContext.console, lineSeparator) } + /** + * Edit [text] in the [editor]. + * + * This blocks until the editor is closed. + * + * @param text The text to edit. + * @param editor The path to the editor to use. Defaults to automatic detection. + * @param env Environment variables to forward to the editor. + * @param requireSave If the editor is closed without saving, null will be returned if true, otherwise + * [text] will be returned. + * @param extension The extension of the temporary file that the editor will open. This can affect syntax + * coloring etc. + * @return The edited text, or null if [requireSave] is true and the editor was closed without saving. + * @throws CliktError if the editor cannot be opened. + */ + protected fun editText( + text: String, + editor: String? = null, + env: Map = emptyMap(), + requireSave: Boolean = false, + extension: String = ".txt", + ): String? { + return TermUi.editText(text, editor, env, requireSave, extension) + } + + /** + * Edit the file with [filename] in the [editor]. + * + * @see editText for usage and parameter descriptions. + */ + protected fun editFile( + filename: String, + editor: String? = null, + env: Map = emptyMap(), + requireSave: Boolean = false, + extension: String = ".txt", + ) { + TermUi.editFile(filename, editor, env, requireSave, extension) + } + /** * Prompt a user for text input. * diff --git a/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/output/TermUi.kt b/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/output/TermUi.kt index 5deb57cdd..be4afe2d2 100644 --- a/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/output/TermUi.kt +++ b/clikt/src/commonMain/kotlin/com/github/ajalt/clikt/output/TermUi.kt @@ -5,7 +5,7 @@ import com.github.ajalt.clikt.core.CliktError import com.github.ajalt.clikt.core.UsageError import com.github.ajalt.clikt.mpp.isWindowsMpp -object TermUi { +internal object TermUi { /** * Print the [message] to the screen. * diff --git a/docs/advanced.md b/docs/advanced.md index e05861ae2..cd215b7f7 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -287,9 +287,6 @@ class CustomCLI : NoOpCliktCommand() { } ``` -If you are using [`TermUI`][TermUI] directly, -you can also pass your custom console as an argument. - ## Command Line Argument Files ("@argfiles") Similar to `javac`, Clikt supports loading command line parameters from a file using the "@argfile" diff --git a/docs/utilities.md b/docs/utilities.md index 6c9ae5f2c..b4edcf033 100644 --- a/docs/utilities.md +++ b/docs/utilities.md @@ -7,7 +7,7 @@ commonly used in command line programs. ## Launching Editors If you need to ask users for multi-line input, or need to have the user edit a file, you can do so -through [`TermUi.editText`][editText] and [`TermUi.editFile`][editFile]. These functions open the +through [`editText`][editText] and [`editFile`][editFile]. These functions open the program defined in the `VISUAL` or `EDITOR` environment variables, or a sensible default if neither are defined. The functions return the edited text if the user saved their changes. @@ -18,7 +18,7 @@ are defined. The functions return the edited text if the user saved their change # Enter your message. # Lines starting with # are ignored """.trimIndent() - return TermUi.editText(message, requireSave = true) + return editText(message, requireSave = true) ?.replace(Regex("#[^\n]*\n"), "") } ``` @@ -26,17 +26,17 @@ are defined. The functions return the edited text if the user saved their change ## Input Prompts Options can [prompt for values automatically][prompting-for-input], but you can also do -so manually with [`TermUi.prompt`][prompt]. By +so manually with [`prompt`][prompt]. By default, it accepts any input string, but you can also pass in a conversion function. If the conversion raises a [`UsageError`][UsageError], the prompt will ask the user to enter a different value. === "Example" ```kotlin - val input = TermUi.prompt("Enter a number") { + val input = prompt("Enter a number") { it.toIntOrNull() ?: throw UsageError("$it is not a valid integer") } - TermUi.echo("Twice your number is ${input * 2}") + echo("Twice your number is ${input * 2}") ``` === "Interactive Session" @@ -50,11 +50,11 @@ the prompt will ask the user to enter a different value. ## Confirmation Prompts You can also ask the user for a yes or no response with -[`TermUi.confirm`][confirm]: +[`confirm`][confirm]: ```kotlin -if (TermUi.confirm("Continue?") == true) { - TermUi.echo("OK!") +if (confirm("Continue?") == true) { + echo("OK!") } ``` @@ -62,7 +62,7 @@ If you simply want to abort the program in the user gives a negative response, you can pass `abort=true`: ```kotlin -TermUi.confirm("Continue?", abort=true) +confirm("Continue?", abort=true) ``` diff --git a/samples/aliases/src/main/kotlin/com/github/ajalt/clikt/samples/aliases/main.kt b/samples/aliases/src/main/kotlin/com/github/ajalt/clikt/samples/aliases/main.kt index f3f3aac13..6edb71cbd 100644 --- a/samples/aliases/src/main/kotlin/com/github/ajalt/clikt/samples/aliases/main.kt +++ b/samples/aliases/src/main/kotlin/com/github/ajalt/clikt/samples/aliases/main.kt @@ -3,7 +3,6 @@ package com.github.ajalt.clikt.samples.aliases import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.NoOpCliktCommand import com.github.ajalt.clikt.core.subcommands -import com.github.ajalt.clikt.output.TermUi import com.github.ajalt.clikt.parameters.options.multiple import com.github.ajalt.clikt.parameters.options.option import java.io.File @@ -23,20 +22,20 @@ class AliasedCli(private val configFile: File) : NoOpCliktCommand( class Push : CliktCommand(help = "push changes") { - override fun run() = TermUi.echo("push") + override fun run() = echo("push") } class Pull : CliktCommand(help = "pull changes") { - override fun run() = TermUi.echo("pull") + override fun run() = echo("pull") } class Clone : CliktCommand(help = "clone a repository") { - override fun run() = TermUi.echo("clone") + override fun run() = echo("clone") } class Commit : CliktCommand(help = "clone a repository") { val message by option("-m", "--message").multiple() - override fun run() = TermUi.echo("commit message=${message.joinToString("\n")}") + override fun run() = echo("commit message=${message.joinToString("\n")}") } fun main(args: Array) { diff --git a/samples/copy/README.md b/samples/copy/README.md index 27f71a017..1d3c0c13d 100644 --- a/samples/copy/README.md +++ b/samples/copy/README.md @@ -1,7 +1,7 @@ # File copy sample This example works like the `cp` command, copying files or directories. -It shows how to use `TermUi` to prompt the user for input. +It shows how to use `prompt` to get an input from user. ``` $ touch src.txt dest.txt diff --git a/samples/copy/src/main/kotlin/com/github/ajalt/clikt/samples/copy/main.kt b/samples/copy/src/main/kotlin/com/github/ajalt/clikt/samples/copy/main.kt index 087753ff3..1be1fbe3d 100644 --- a/samples/copy/src/main/kotlin/com/github/ajalt/clikt/samples/copy/main.kt +++ b/samples/copy/src/main/kotlin/com/github/ajalt/clikt/samples/copy/main.kt @@ -1,7 +1,6 @@ package com.github.ajalt.clikt.samples.copy import com.github.ajalt.clikt.core.CliktCommand -import com.github.ajalt.clikt.output.TermUi import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.arguments.multiple import com.github.ajalt.clikt.parameters.options.flag @@ -21,7 +20,7 @@ class Copy : CliktCommand(help = "Copy SOURCE to DEST, or multiple SOURCE(s) to else file.copyTo(dest) } catch (e: FileAlreadyExistsException) { if (interactive) { - val response = TermUi.confirm("overwrite '$dest'?", default = true) + val response = confirm("overwrite '$dest'?", default = true) if (response == false) continue } if (recursive) file.copyRecursively(dest, overwrite = true) diff --git a/samples/plugins/src/main/kotlin/com/github/ajalt/clikt/samples/plugins/commit.kt b/samples/plugins/src/main/kotlin/com/github/ajalt/clikt/samples/plugins/commit.kt index f71522901..1e8b82cf9 100644 --- a/samples/plugins/src/main/kotlin/com/github/ajalt/clikt/samples/plugins/commit.kt +++ b/samples/plugins/src/main/kotlin/com/github/ajalt/clikt/samples/plugins/commit.kt @@ -2,7 +2,6 @@ package com.github.ajalt.clikt.samples.plugins import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.requireObject -import com.github.ajalt.clikt.output.TermUi import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.arguments.multiple import com.github.ajalt.clikt.parameters.options.multiple @@ -43,7 +42,7 @@ class Commit : CliktCommand( } } - val message = TermUi.editText(text) + val message = editText(text) if (message == null) { echo("Aborted!") return diff --git a/samples/plugins/src/main/kotlin/com/github/ajalt/clikt/samples/plugins/setuser.kt b/samples/plugins/src/main/kotlin/com/github/ajalt/clikt/samples/plugins/setuser.kt index 444ee683c..996c344f5 100644 --- a/samples/plugins/src/main/kotlin/com/github/ajalt/clikt/samples/plugins/setuser.kt +++ b/samples/plugins/src/main/kotlin/com/github/ajalt/clikt/samples/plugins/setuser.kt @@ -2,7 +2,6 @@ package com.github.ajalt.clikt.samples.plugins import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.requireObject -import com.github.ajalt.clikt.output.TermUi import com.github.ajalt.clikt.parameters.options.option import com.github.ajalt.clikt.parameters.options.prompt import org.kodein.di.Kodein @@ -27,7 +26,7 @@ class SetUser : CliktCommand( repo.config["username"] = username repo.config["email"] = email repo.config["password"] = "*".repeat(password.length) - TermUi.echo("Changed credentials.") + echo("Changed credentials.") } } diff --git a/samples/repo/src/main/kotlin/com/github/ajalt/clikt/samples/repo/main.kt b/samples/repo/src/main/kotlin/com/github/ajalt/clikt/samples/repo/main.kt index 20186bffc..7d1649a8c 100644 --- a/samples/repo/src/main/kotlin/com/github/ajalt/clikt/samples/repo/main.kt +++ b/samples/repo/src/main/kotlin/com/github/ajalt/clikt/samples/repo/main.kt @@ -3,7 +3,6 @@ package com.github.ajalt.clikt.samples.repo import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.core.requireObject import com.github.ajalt.clikt.core.subcommands -import com.github.ajalt.clikt.output.TermUi import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.arguments.multiple import com.github.ajalt.clikt.parameters.arguments.optional @@ -130,7 +129,7 @@ class Commit : CliktCommand( } } - val message = TermUi.editText(text) + val message = editText(text) if (message == null) { echo("Aborted!") return