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 069ea5fc..b2ed16d5 100644 --- a/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt +++ b/core/src/main/java/com/facebook/ktfmt/cli/ParsedArgs.kt @@ -60,30 +60,30 @@ data class ParsedArgs( |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 + |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 + | -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 + | --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 @@ -109,9 +109,21 @@ data class ParsedArgs( for (arg in args) { when { - arg == "--meta-style" -> formattingOptions = Formatter.META_FORMAT - arg == "--google-style" -> formattingOptions = Formatter.GOOGLE_FORMAT - arg == "--kotlinlang-style" -> formattingOptions = Formatter.KOTLINLANG_FORMAT + arg.startsWith("--style=") -> { + val parsedStyle = + parseKeyValueArg("--style", arg) + ?: return ParseResult.Error( + unexpectedArg(arg) + ) + formattingOptions = when (parsedStyle) { + "meta" -> Formatter.META_FORMAT + "google" -> Formatter.GOOGLE_FORMAT + "kotlinlang" -> Formatter.KOTLINLANG_FORMAT + else -> return ParseResult.Error( + "Unknown style '${parsedStyle}'. Style must be one of [dropbox, google, kotlinlang]." + ) + } + } arg == "--dry-run" || arg == "-n" -> dryRun = true arg == "--set-exit-if-changed" -> setExitIfChanged = true arg == "--do-not-remove-unused-imports" -> removeUnusedImports = false @@ -120,8 +132,8 @@ data class ParsedArgs( parseKeyValueArg("--stdin-name", arg) ?: return ParseResult.Error( "Found option '${arg}', expected '${"--stdin-name"}='") - arg.startsWith("--") -> return ParseResult.Error("Unexpected option: $arg") - arg.startsWith("@") -> return ParseResult.Error("Unexpected option: $arg") + arg.startsWith("--") -> return ParseResult.Error(unexpectedArg(arg)) + arg.startsWith("@") -> return ParseResult.Error(unexpectedArg(arg)) else -> fileNames.add(arg) } } @@ -148,6 +160,8 @@ data class ParsedArgs( )) } + private fun unexpectedArg(arg: String) = "Unexpected option: $arg" + private fun parseKeyValueArg(key: String, arg: String): String? { val parts = arg.split('=', limit = 2) return parts[1].takeIf { parts[0] == key || parts.size == 2 } 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 a2adbc44..31cc47a5 100644 --- a/core/src/test/java/com/facebook/ktfmt/cli/MainTest.kt +++ b/core/src/test/java/com/facebook/ktfmt/cli/MainTest.kt @@ -225,7 +225,7 @@ class MainTest { } @Test - fun `kotlinlang-style is passed to formatter (file)`() { + fun `--style=kotlinlang is passed to formatter (file)`() { val code = """fun f() { for (child in @@ -241,14 +241,14 @@ class MainTest { emptyInput, PrintStream(out), PrintStream(err), - arrayOf("--kotlinlang-style", fooBar.toString())) + arrayOf("--style=kotlinlang", fooBar.toString())) .run() assertThat(fooBar.readText()).isEqualTo(code) } @Test - fun `kotlinlang-style is passed to formatter (stdin)`() { + fun `--style=kotlinlang is passed to formatter (stdin)`() { val code = """fun f() { |for (child in @@ -271,7 +271,7 @@ class MainTest { code.byteInputStream(), PrintStream(out), PrintStream(err), - arrayOf("--kotlinlang-style", "-")) + arrayOf("--style=kotlinlang", "-")) .run() assertThat(out.toString(UTF_8)).isEqualTo(formatted) 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 cac9cc12..6d5fbbc9 100644 --- a/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt +++ b/core/src/test/java/com/facebook/ktfmt/cli/ParsedArgsTest.kt @@ -61,23 +61,29 @@ class ParsedArgsTest { } @Test - fun `parseOptions recognizes --meta-style`() { - val parsed = assertSucceeds(ParsedArgs.parseOptions(arrayOf("--meta-style", "foo.kt"))) + fun `parseOptions recognizes --style=meta`() { + val parsed = assertSucceeds(ParsedArgs.parseOptions(arrayOf("--style=meta", "foo.kt"))) assertThat(parsed.formattingOptions).isEqualTo(Formatter.META_FORMAT) } @Test - fun `parseOptions recognizes --dropbox-style`() { - val parsed = assertSucceeds(ParsedArgs.parseOptions(arrayOf("--kotlinlang-style", "foo.kt"))) + fun `parseOptions recognizes --style=kotlinlang`() { + val parsed = assertSucceeds(ParsedArgs.parseOptions(arrayOf("--style=kotlinlang", "foo.kt"))) assertThat(parsed.formattingOptions).isEqualTo(Formatter.KOTLINLANG_FORMAT) } @Test - fun `parseOptions recognizes --google-style`() { - val parsed = assertSucceeds(ParsedArgs.parseOptions(arrayOf("--google-style", "foo.kt"))) + fun `parseOptions recognizes --style=google`() { + val parsed = assertSucceeds(ParsedArgs.parseOptions(arrayOf("--style=google", "foo.kt"))) assertThat(parsed.formattingOptions).isEqualTo(Formatter.GOOGLE_FORMAT) } + @Test + fun `parseOptions rejects unknown style`() { + val parseResult = ParsedArgs.parseOptions(arrayOf("--style=custom-style", "foo.kt")) + assertThat(parseResult).isInstanceOf(ParseResult.Error::class.java) + } + @Test fun `parseOptions recognizes --dry-run`() { val parsed = assertSucceeds(ParsedArgs.parseOptions(arrayOf("--dry-run", "foo.kt"))) @@ -170,7 +176,7 @@ class ParsedArgsTest { @Test fun `processArgs use the @file option with file containing arguments`() { val file = root.resolve("existing-file") - file.writeText("--google-style\n--dry-run\n--set-exit-if-changed\nFile1.kt\nFile2.kt\n") + file.writeText("--style=google\n--dry-run\n--set-exit-if-changed\nFile1.kt\nFile2.kt\n") val result = ParsedArgs.processArgs(arrayOf("@" + file.absolutePath)) assertThat(result).isInstanceOf(ParseResult.Ok::class.java) @@ -187,7 +193,7 @@ class ParsedArgsTest { fun `parses multiple args successfully`() { val testResult = ParsedArgs.parseOptions( - arrayOf("--google-style", "--dry-run", "--set-exit-if-changed", "File.kt"), + arrayOf("--style=google", "--dry-run", "--set-exit-if-changed", "File.kt"), ) assertThat(testResult) .isEqualTo( @@ -202,7 +208,7 @@ class ParsedArgsTest { @Test fun `last style in args wins`() { val testResult = - ParsedArgs.parseOptions(arrayOf("--google-style", "--kotlinlang-style", "File.kt")) + ParsedArgs.parseOptions(arrayOf("--style=google", "--style=kotlinlang", "File.kt")) assertThat(testResult) .isEqualTo( parseResultOk( @@ -213,7 +219,7 @@ class ParsedArgsTest { @Test fun `error when parsing multiple args and one is unknown`() { - val testResult = ParsedArgs.parseOptions(arrayOf("@unknown", "--google-style", "File.kt")) + val testResult = ParsedArgs.parseOptions(arrayOf("@unknown", "--style=google", "File.kt")) assertThat(testResult).isEqualTo(ParseResult.Error("Unexpected option: @unknown")) }