mirror of
https://github.com/apple/pkl.git
synced 2026-03-24 01:51:19 +01:00
Implement canonical formatter (#1107)
CLI commands also added: `pkl format check` and `pkl format apply`.
This commit is contained in:
52
pkl-cli/src/main/kotlin/org/pkl/cli/CliFormatterApply.kt
Normal file
52
pkl-cli/src/main/kotlin/org/pkl/cli/CliFormatterApply.kt
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.pkl.cli
|
||||
|
||||
import java.io.IOException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.writeText
|
||||
import org.pkl.commons.cli.CliBaseOptions
|
||||
import org.pkl.commons.cli.CliException
|
||||
|
||||
class CliFormatterApply(cliBaseOptions: CliBaseOptions, path: Path, private val silent: Boolean) :
|
||||
CliFormatterCommand(cliBaseOptions, path) {
|
||||
|
||||
override fun doRun() {
|
||||
var status = 0
|
||||
|
||||
for (path in paths()) {
|
||||
val contents = Files.readString(path)
|
||||
val (formatted, stat) = format(path, contents)
|
||||
status = if (status == 0) stat else status
|
||||
if (stat != 0) continue
|
||||
if (!silent && contents != formatted) {
|
||||
consoleWriter.write(path.toAbsolutePath().toString())
|
||||
consoleWriter.flush()
|
||||
}
|
||||
try {
|
||||
path.writeText(formatted, Charsets.UTF_8)
|
||||
} catch (e: IOException) {
|
||||
consoleWriter.write("Could not overwrite `$path`: ${e.message}")
|
||||
consoleWriter.flush()
|
||||
status = 1
|
||||
}
|
||||
}
|
||||
if (status != 0) {
|
||||
throw CliException("Formatting violations found.", status)
|
||||
}
|
||||
}
|
||||
}
|
||||
43
pkl-cli/src/main/kotlin/org/pkl/cli/CliFormatterCheck.kt
Normal file
43
pkl-cli/src/main/kotlin/org/pkl/cli/CliFormatterCheck.kt
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.pkl.cli
|
||||
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import org.pkl.commons.cli.CliBaseOptions
|
||||
import org.pkl.commons.cli.CliException
|
||||
|
||||
class CliFormatterCheck(cliBaseOptions: CliBaseOptions, path: Path) :
|
||||
CliFormatterCommand(cliBaseOptions, path) {
|
||||
|
||||
override fun doRun() {
|
||||
var status = 0
|
||||
|
||||
for (path in paths()) {
|
||||
val contents = Files.readString(path)
|
||||
val (formatted, stat) = format(path, contents)
|
||||
status = if (status == 0) stat else status
|
||||
if (contents != formatted) {
|
||||
consoleWriter.write(path.toAbsolutePath().toString())
|
||||
consoleWriter.flush()
|
||||
status = 1
|
||||
}
|
||||
}
|
||||
if (status != 0) {
|
||||
throw CliException("Formatting violations found.", status)
|
||||
}
|
||||
}
|
||||
}
|
||||
53
pkl-cli/src/main/kotlin/org/pkl/cli/CliFormatterCommand.kt
Normal file
53
pkl-cli/src/main/kotlin/org/pkl/cli/CliFormatterCommand.kt
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.pkl.cli
|
||||
|
||||
import java.io.Writer
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.ExperimentalPathApi
|
||||
import kotlin.io.path.extension
|
||||
import kotlin.io.path.isDirectory
|
||||
import kotlin.io.path.name
|
||||
import kotlin.io.path.walk
|
||||
import org.pkl.commons.cli.CliBaseOptions
|
||||
import org.pkl.commons.cli.CliCommand
|
||||
import org.pkl.formatter.Formatter
|
||||
import org.pkl.parser.GenericParserError
|
||||
|
||||
abstract class CliFormatterCommand
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
options: CliBaseOptions,
|
||||
protected val path: Path,
|
||||
protected val consoleWriter: Writer = System.out.writer(),
|
||||
) : CliCommand(options) {
|
||||
protected fun format(file: Path, contents: String): Pair<String, Int> {
|
||||
try {
|
||||
return Formatter().format(contents) to 0
|
||||
} catch (pe: GenericParserError) {
|
||||
consoleWriter.write("Could not format `$file`: $pe")
|
||||
consoleWriter.flush()
|
||||
return "" to 1
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalPathApi::class)
|
||||
protected fun paths(): Sequence<Path> {
|
||||
return if (path.isDirectory()) {
|
||||
path.walk().filter { it.extension == "pkl" || it.name == "PklProject" }
|
||||
} else sequenceOf(path)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* https://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 org.pkl.cli.commands
|
||||
|
||||
import com.github.ajalt.clikt.core.Context
|
||||
import com.github.ajalt.clikt.core.NoOpCliktCommand
|
||||
import com.github.ajalt.clikt.core.subcommands
|
||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||
import com.github.ajalt.clikt.parameters.options.flag
|
||||
import com.github.ajalt.clikt.parameters.options.option
|
||||
import com.github.ajalt.clikt.parameters.types.path
|
||||
import java.nio.file.Path
|
||||
import org.pkl.cli.CliFormatterApply
|
||||
import org.pkl.cli.CliFormatterCheck
|
||||
import org.pkl.commons.cli.commands.BaseCommand
|
||||
|
||||
class FormatterCommand : NoOpCliktCommand(name = "format") {
|
||||
override fun help(context: Context) = "Run commands related to formatting"
|
||||
|
||||
override fun helpEpilog(context: Context) = "For more information, visit $helpLink"
|
||||
|
||||
init {
|
||||
subcommands(FormatterCheckCommand(), FormatterApplyCommand())
|
||||
}
|
||||
}
|
||||
|
||||
class FormatterCheckCommand : BaseCommand(name = "check", helpLink = helpLink) {
|
||||
override val helpString: String =
|
||||
"Check if the given files are properly formatted, printing the file name to stdout in case they are not. Returns non-zero in case of failure."
|
||||
|
||||
val path: Path by
|
||||
argument(name = "path", help = "File or directory to check.")
|
||||
.path(mustExist = true, canBeDir = true)
|
||||
|
||||
override fun run() {
|
||||
CliFormatterCheck(baseOptions.baseOptions(emptyList()), path).run()
|
||||
}
|
||||
}
|
||||
|
||||
class FormatterApplyCommand : BaseCommand(name = "apply", helpLink = helpLink) {
|
||||
override val helpString: String =
|
||||
"Overwrite all the files in place with the formatted version. Returns non-zero in case of failure."
|
||||
|
||||
val path: Path by
|
||||
argument(name = "path", help = "File or directory to format.")
|
||||
.path(mustExist = true, canBeDir = true)
|
||||
|
||||
val silent: Boolean by
|
||||
option(
|
||||
names = arrayOf("-s", "--silent"),
|
||||
help = "Do not write the name of the files that failed formatting to stdout.",
|
||||
)
|
||||
.flag()
|
||||
|
||||
override fun run() {
|
||||
CliFormatterApply(baseOptions.baseOptions(emptyList()), path, silent)
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ class RootCommand : NoOpCliktCommand(name = "pkl") {
|
||||
ProjectCommand(),
|
||||
DownloadPackageCommand(),
|
||||
AnalyzeCommand(),
|
||||
FormatterCommand(),
|
||||
CompletionCommand(
|
||||
name = "shell-completion",
|
||||
help = "Generate a completion script for the given shell",
|
||||
|
||||
Reference in New Issue
Block a user