diff --git a/CHANGELOG.md b/CHANGELOG.md index dc9d4eff..f27030d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). ### Added - Created CHANGELOG.md +- Added --help option to CLI (https://github.com/facebook/ktfmt/pull/477) ### Changed - Preserves blank spaces between when clauses (https://github.com/facebook/ktfmt/issues/342) diff --git a/core/src/main/java/com/facebook/ktfmt/cli/Main.kt b/core/src/main/java/com/facebook/ktfmt/cli/Main.kt index 7f57aa89..8a1ff36f 100644 --- a/core/src/main/java/com/facebook/ktfmt/cli/Main.kt +++ b/core/src/main/java/com/facebook/ktfmt/cli/Main.kt @@ -37,10 +37,13 @@ private const val EXIT_CODE_SUCCESS = 0 private val USAGE = """ - Usage: ktfmt [--meta-style | --google-style | --kotlinlang-style] [--dry-run] [--set-exit-if-changed] [--stdin-name=] [--do-not-remove-unused-imports] File1.kt File2.kt ... - Or: ktfmt @file - """ - .trimIndent() + |Usage: + | ktfmt [OPTIONS] File1.kt File2.kt ... + | ktfmt @ARGFILE + | + |For more details see `ktfmt --help` + |""" + .trimMargin() class Main( private val input: InputStream, @@ -80,6 +83,10 @@ class Main( val parsedArgs = when (processArgs) { is ParseResult.Ok -> processArgs.parsedValue + is ParseResult.ShowMessage -> { + out.println(processArgs.message) + return EXIT_CODE_SUCCESS + } is ParseResult.Error -> { err.println(processArgs.errorMessage) return EXIT_CODE_FAILURE diff --git a/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt b/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt index 41a6e30b..069ea5fc 100644 --- a/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt +++ b/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt @@ -48,6 +48,54 @@ data class ParsedArgs( return parseOptions(arguments) } + val HELP_TEXT = + """ + |ktfmt - command line Kotlin source code pretty-printer + | + |Usage: + | ktfmt [OPTIONS] ... + | ktfmt @ARGFILE + | + |ktfmt formats Kotlin source code files in-place, reporting for each file whether the + |formatting succeeded or failed on standard error. If none of the style options are + |passed, Meta's style is used. + | + |Alternatively, ktfmt can read Kotlin source code from standard input and write the + |formatted result on standard output. + | + |Example: + | $ ktfmt --kotlinlang-style Main.kt src/Parser.kt + | Done formatting Main.kt + | Error formatting src/Parser.kt: @@@ERROR@@@; skipping. + | + |Commands options: + | -h, --help Show this help message + | -n, --dry-run Don't write to files, only report files which + | would have changed + | --meta-style Use 2-space block indenting (default) + | --google-style Google internal style (2 spaces) + | --kotlinlang-style Kotlin language guidelines style (4 spaces) + | --stdin-name= Name to report when formatting code from stdin + | --set-exit-if-changed Sets exit code to 1 if any input file was not + | formatted/touched + | --do-not-remove-unused-imports Leaves all imports in place, even if not used + | + |ARGFILE: + | If the only argument begins with '@', the remainder of the argument is treated + | as the name of a file to read options and arguments from, one per line. + | + | e.g. + | $ cat arg-file.txt + | --google-style + | -n + | File1.kt + | File2.kt + | $ ktfmt @arg-file1.txt + | Done formatting File1.kt + | Done formatting File2.kt + |""" + .trimMargin() + /** parseOptions parses command-line arguments passed to ktfmt. */ fun parseOptions(args: Array): ParseResult { val fileNames = mutableListOf() @@ -57,6 +105,8 @@ data class ParsedArgs( var removeUnusedImports = true var stdinName: String? = null + if ("--help" in args || "-h" in args) return ParseResult.ShowMessage(HELP_TEXT) + for (arg in args) { when { arg == "--meta-style" -> formattingOptions = Formatter.META_FORMAT @@ -108,5 +158,7 @@ data class ParsedArgs( sealed interface ParseResult { data class Ok(val parsedValue: ParsedArgs) : ParseResult + data class ShowMessage(val message: String) : ParseResult + data class Error(val errorMessage: String) : ParseResult } diff --git a/core/src/test/java/com/facebook/ktfmt/cli/MainTest.kt b/core/src/test/java/com/facebook/ktfmt/cli/MainTest.kt index 1158b7e8..a2adbc44 100644 --- a/core/src/test/java/com/facebook/ktfmt/cli/MainTest.kt +++ b/core/src/test/java/com/facebook/ktfmt/cli/MainTest.kt @@ -493,4 +493,18 @@ class MainTest { assertThat(exitCode).isEqualTo(0) assertThat(file.readText(UTF_8)).isEqualTo("""fun f() = println("hello, world")""" + "\n") } + + @Test + fun `--help gives return code of 0`() { + val exitCode = + Main( + emptyInput, + PrintStream(out), + PrintStream(err), + arrayOf("--help"), + ) + .run() + + assertThat(exitCode).isEqualTo(0) + } } diff --git a/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt b/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt index 8f252e6d..cac9cc12 100644 --- a/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt +++ b/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt @@ -139,6 +139,25 @@ class ParsedArgsTest { assertThat(parseResult).isInstanceOf(ParseResult.Error::class.java) } + @Test + fun `parseOptions recognises --help`() { + val parseResult = ParsedArgs.parseOptions(arrayOf("--help")) + assertThat(parseResult).isInstanceOf(ParseResult.ShowMessage::class.java) + } + + @Test + fun `parseOptions recognises -h`() { + val parseResult = ParsedArgs.parseOptions(arrayOf("-h")) + assertThat(parseResult).isInstanceOf(ParseResult.ShowMessage::class.java) + } + + @Test + fun `arg --help overrides all others`() { + val parseResult = + ParsedArgs.parseOptions(arrayOf("--style=google", "@unknown", "--help", "file.kt")) + assertThat(parseResult).isInstanceOf(ParseResult.ShowMessage::class.java) + } + @Test fun `processArgs use the @file option with non existing file`() { val e = diff --git a/online_formatter/src/main/kotlin/main.kt b/online_formatter/src/main/kotlin/main.kt index 9890e7dc..452162a1 100644 --- a/online_formatter/src/main/kotlin/main.kt +++ b/online_formatter/src/main/kotlin/main.kt @@ -35,8 +35,7 @@ class Handler : RequestHandler { try { val request = gson.fromJson(event.body, Request::class.java) val style = request.style - val parseResult = ParsedArgs.parseOptions(listOfNotNull(style).toTypedArray()) - when (parseResult) { + when (val parseResult = ParsedArgs.parseOptions(listOfNotNull(style).toTypedArray())) { is ParseResult.Ok -> { val parsedArgs = parseResult.parsedValue Response( @@ -51,6 +50,9 @@ class Handler : RequestHandler { is ParseResult.Error -> { Response(null, parseResult.errorMessage) } + is ParseResult.ShowMessage -> { + Response(null, parseResult.message) + } } } catch (e: Exception) { Response(null, e.message)