Skip to content

Commit

Permalink
When formatting stdin, print formatted code to stdout regardless of w…
Browse files Browse the repository at this point in the history
…hether it changed

Reviewed By: strulovich

Differential Revision: D33888492

fbshipit-source-id: 00602c756c932b10594a19d7ae2b43c6d0639f41
  • Loading branch information
cgrushko authored and facebook-github-bot committed Feb 1, 2022
1 parent fd042ed commit f85414c
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 26 deletions.
64 changes: 40 additions & 24 deletions core/src/main/java/com/facebook/ktfmt/cli/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.io.PrintStream
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
import kotlin.system.exitProcess

class Main(
Expand Down Expand Up @@ -70,13 +70,17 @@ class Main(
fun run(): Int {
if (parsedArgs.fileNames.isEmpty()) {
err.println(
"Usage: ktfmt [--dropbox-style | --google-style | --kotlinlang-style] File1.kt File2.kt ...")
"Usage: ktfmt [--dropbox-style | --google-style | --kotlinlang-style] [--dry-run] [--set-exit-if-changed] File1.kt File2.kt ...")
return 1
}

if (parsedArgs.fileNames.size == 1 && parsedArgs.fileNames[0] == "-") {
val success = format(null)
return if (success) 0 else 1
return try {
val alreadyFormatted = format(null)
if (!alreadyFormatted && parsedArgs.setExitIfChanged) 1 else 0
} catch (e: Exception) {
1
}
}

val files: List<File>
Expand All @@ -92,9 +96,17 @@ class Main(
return 1
}

val success = AtomicBoolean(true)
files.parallelStream().forEach { success.compareAndSet(true, format(it)) }
return if (success.get()) 0 else 1
val retval = AtomicInteger(0)
files.parallelStream().forEach {
try {
if (!format(it) && parsedArgs.setExitIfChanged) {
retval.set(1)
}
} catch (e: Exception) {
retval.set(1)
}
}
return retval.get()
}

/**
Expand All @@ -104,46 +116,50 @@ class Main(
* to [out]. Otherwise, this will run the appropriate formatting as normal.
*
* @param file The file to format. If null, the code is read from <stdin>.
* @return True if changes were made or no changes were needed, false if there was a failure or if
* changes were made and the --set-exit-if-changed flag is set.
* @return true iff input is valid and already formatted.
*/
private fun format(file: File?): Boolean {
val fileName = file?.toString() ?: "<stdin>"
try {
val code = file?.readText() ?: BufferedReader(InputStreamReader(input)).readText()

val formattedCode = Formatter.format(parsedArgs.formattingOptions, code)

if (code == formattedCode) {
// The code was already formatted, nothing more to do here
err.println("No changes: $fileName")
return true
val alreadyFormatted = code == formattedCode

// stdin
if (file == null) {
if (parsedArgs.dryRun) {
if (!alreadyFormatted) {
out.println("<stdin>")
}
} else {
out.print(formattedCode)
}
return alreadyFormatted
}

if (parsedArgs.dryRun) {
out.println(fileName)
} else {
if (file != null) {
file.writeText(formattedCode)
} else {
out.print(formattedCode)
if (!alreadyFormatted) {
out.println(fileName)
}
} else {
file.writeText(formattedCode)
err.println("Done formatting $fileName")
}

// If setExitIfChanged is true, then any change should result in an exit code of 1.
return if (parsedArgs.setExitIfChanged) false else true
return alreadyFormatted
} catch (e: IOException) {
err.println("Error formatting $fileName: ${e.message}; skipping.")
throw e
} catch (e: ParseError) {
handleParseError(fileName, e)
throw e
} catch (e: FormattingError) {
for (diagnostic in e.diagnostics()) {
System.err.println("$fileName:$diagnostic")
}
e.printStackTrace(err)
throw e
}
return false
}

private fun handleParseError(fileName: String, e: ParseError) {
Expand Down
26 changes: 24 additions & 2 deletions core/src/test/java/com/facebook/ktfmt/cli/MainTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@ class MainTest {
val code = "fun f1 ( ) : Int = 0"
Main(code.byteInputStream(), PrintStream(out), PrintStream(err), arrayOf("-")).run()

val expected = """fun f1(): Int = 0
|""".trimMargin()
val expected = "fun f1(): Int = 0\n"
assertThat(out.toString("UTF-8")).isEqualTo(expected)
}

Expand Down Expand Up @@ -234,6 +233,29 @@ class MainTest {
.containsExactly(f1, f2, f5, f6, f7)
}

@Test
fun `formatting from stdin prints formatted code to stdout regardless of whether it was already formatted`() {
val expected = """fun f() = println("hello, world")""" + "\n"

Main(
"""fun f ( ) = println("hello, world")""".byteInputStream(),
PrintStream(out),
PrintStream(err),
arrayOf("-"))
.run()
assertThat(out.toString("UTF-8")).isEqualTo(expected)

out.reset()

Main(
"""fun f () = println("hello, world")""".byteInputStream(),
PrintStream(out),
PrintStream(err),
arrayOf("-"))
.run()
assertThat(out.toString("UTF-8")).isEqualTo(expected)
}

@Test
fun `--dry-run prints filename and does not change file`() {
val code = """fun f () = println( "hello, world" )"""
Expand Down

0 comments on commit f85414c

Please sign in to comment.