com.facebook.ktfmt.cli.Main.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ktfmt Show documentation
Show all versions of ktfmt Show documentation
A program that reformats Kotlin source code to comply with the common community standard for Kotlin code conventions.
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.facebook.ktfmt.cli
import com.facebook.ktfmt.format.Formatter
import com.facebook.ktfmt.format.ParseError
import com.google.googlejavaformat.FormattingError
import java.io.BufferedReader
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
import java.io.PrintStream
import java.util.concurrent.atomic.AtomicInteger
import kotlin.system.exitProcess
class Main(
private val input: InputStream,
private val out: PrintStream,
private val err: PrintStream,
args: Array
) {
companion object {
@JvmStatic
fun main(args: Array) {
exitProcess(Main(System.`in`, System.out, System.err, args).run())
}
/**
* expandArgsToFileNames expands 'args' to a list of .kt files to format.
*
* Most commonly, 'args' is either a list of .kt files, or a name of a directory whose contents
* the user wants to format.
*/
fun expandArgsToFileNames(args: List): List {
if (args.size == 1 && File(args[0]).isFile) {
return listOf(File(args[0]))
}
val result = mutableListOf()
for (arg in args) {
if (arg == "-") {
error(
"Error: '-', which causes ktfmt to read from stdin, should not be mixed with file name")
}
result.addAll(
File(arg).walkTopDown().filter {
it.isFile && (it.extension == "kt" || it.extension == "kts")
})
}
return result
}
}
private val parsedArgs: ParsedArgs = ParsedArgs.parseOptions(err, args)
fun run(): Int {
if (parsedArgs.fileNames.isEmpty()) {
err.println(
"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] == "-") {
return try {
val alreadyFormatted = format(null)
if (!alreadyFormatted && parsedArgs.setExitIfChanged) 1 else 0
} catch (e: Exception) {
1
}
}
val files: List
try {
files = expandArgsToFileNames(parsedArgs.fileNames)
} catch (e: java.lang.IllegalStateException) {
err.println(e.message)
return 1
}
if (files.isEmpty()) {
err.println("Error: no .kt files found")
return 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()
}
/**
* Handles the logic for formatting and flags.
*
* If dry run mode is active, this simply prints the name of the [source] (file path or ``)
* to [out]. Otherwise, this will run the appropriate formatting as normal.
*
* @param file The file to format. If null, the code is read from .
* @return true iff input is valid and already formatted.
*/
private fun format(file: File?): Boolean {
val fileName = file?.toString() ?: ""
try {
val code = file?.readText() ?: BufferedReader(InputStreamReader(input)).readText()
val formattedCode = Formatter.format(parsedArgs.formattingOptions, code)
val alreadyFormatted = code == formattedCode
// stdin
if (file == null) {
if (parsedArgs.dryRun) {
if (!alreadyFormatted) {
out.println("")
}
} else {
out.print(formattedCode)
}
return alreadyFormatted
}
if (parsedArgs.dryRun) {
if (!alreadyFormatted) {
out.println(fileName)
}
} else {
// TODO(T111284144): Add tests
if (!alreadyFormatted) {
file.writeText(formattedCode)
}
err.println("Done formatting $fileName")
}
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
}
}
private fun handleParseError(fileName: String, e: ParseError) {
err.println("$fileName:${e.message}")
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy