mirror of
https://github.com/apple/pkl.git
synced 2026-04-24 09:18:35 +02:00
Add grammar compatibility option to the formatter (#1249)
This commit is contained in:
@@ -743,6 +743,16 @@ It returns a non-zero status code in case violations are found.
|
|||||||
|
|
||||||
If the path is a directory, recursively looks for files with a `.pkl` extension, or files named `PklProject`.
|
If the path is a directory, recursively looks for files with a `.pkl` extension, or files named `PklProject`.
|
||||||
|
|
||||||
|
==== Options
|
||||||
|
|
||||||
|
.--grammar-version
|
||||||
|
[%collapsible]
|
||||||
|
====
|
||||||
|
Default: `2` (latest version) +
|
||||||
|
Select the grammar compatibility version for the formatter.
|
||||||
|
New versions are created for each backward incompatible grammar change.
|
||||||
|
====
|
||||||
|
|
||||||
[[command-format-apply]]
|
[[command-format-apply]]
|
||||||
=== `pkl format apply`
|
=== `pkl format apply`
|
||||||
|
|
||||||
@@ -760,6 +770,14 @@ If the path is a directory, recursively looks for files with a `.pkl` extension,
|
|||||||
Do not write the name of wrongly formatted files to stdout.
|
Do not write the name of wrongly formatted files to stdout.
|
||||||
====
|
====
|
||||||
|
|
||||||
|
.--grammar-version
|
||||||
|
[%collapsible]
|
||||||
|
====
|
||||||
|
Default: `2` (latest version) +
|
||||||
|
Select the grammar compatibility version for the formatter.
|
||||||
|
New versions are created for each backward incompatible grammar change.
|
||||||
|
====
|
||||||
|
|
||||||
[[common-options]]
|
[[common-options]]
|
||||||
=== Common options
|
=== Common options
|
||||||
|
|
||||||
|
|||||||
@@ -21,12 +21,14 @@ import java.nio.file.Path
|
|||||||
import kotlin.io.path.writeText
|
import kotlin.io.path.writeText
|
||||||
import org.pkl.commons.cli.CliBaseOptions
|
import org.pkl.commons.cli.CliBaseOptions
|
||||||
import org.pkl.commons.cli.CliException
|
import org.pkl.commons.cli.CliException
|
||||||
|
import org.pkl.formatter.GrammarVersion
|
||||||
|
|
||||||
class CliFormatterApply(
|
class CliFormatterApply(
|
||||||
cliBaseOptions: CliBaseOptions,
|
cliBaseOptions: CliBaseOptions,
|
||||||
paths: List<Path>,
|
paths: List<Path>,
|
||||||
|
grammarVersion: GrammarVersion,
|
||||||
private val silent: Boolean,
|
private val silent: Boolean,
|
||||||
) : CliFormatterCommand(cliBaseOptions, paths) {
|
) : CliFormatterCommand(cliBaseOptions, paths, grammarVersion) {
|
||||||
|
|
||||||
override fun doRun() {
|
override fun doRun() {
|
||||||
var status = 0
|
var status = 0
|
||||||
|
|||||||
@@ -19,9 +19,13 @@ import java.nio.file.Files
|
|||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import org.pkl.commons.cli.CliBaseOptions
|
import org.pkl.commons.cli.CliBaseOptions
|
||||||
import org.pkl.commons.cli.CliException
|
import org.pkl.commons.cli.CliException
|
||||||
|
import org.pkl.formatter.GrammarVersion
|
||||||
|
|
||||||
class CliFormatterCheck(cliBaseOptions: CliBaseOptions, paths: List<Path>) :
|
class CliFormatterCheck(
|
||||||
CliFormatterCommand(cliBaseOptions, paths) {
|
cliBaseOptions: CliBaseOptions,
|
||||||
|
paths: List<Path>,
|
||||||
|
grammarVersion: GrammarVersion,
|
||||||
|
) : CliFormatterCommand(cliBaseOptions, paths, grammarVersion) {
|
||||||
|
|
||||||
override fun doRun() {
|
override fun doRun() {
|
||||||
var status = 0
|
var status = 0
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import kotlin.io.path.walk
|
|||||||
import org.pkl.commons.cli.CliBaseOptions
|
import org.pkl.commons.cli.CliBaseOptions
|
||||||
import org.pkl.commons.cli.CliCommand
|
import org.pkl.commons.cli.CliCommand
|
||||||
import org.pkl.formatter.Formatter
|
import org.pkl.formatter.Formatter
|
||||||
|
import org.pkl.formatter.GrammarVersion
|
||||||
import org.pkl.parser.GenericParserError
|
import org.pkl.parser.GenericParserError
|
||||||
|
|
||||||
abstract class CliFormatterCommand
|
abstract class CliFormatterCommand
|
||||||
@@ -32,11 +33,12 @@ abstract class CliFormatterCommand
|
|||||||
constructor(
|
constructor(
|
||||||
options: CliBaseOptions,
|
options: CliBaseOptions,
|
||||||
protected val paths: List<Path>,
|
protected val paths: List<Path>,
|
||||||
|
protected val grammarVersion: GrammarVersion,
|
||||||
protected val consoleWriter: Writer = System.out.writer(),
|
protected val consoleWriter: Writer = System.out.writer(),
|
||||||
) : CliCommand(options) {
|
) : CliCommand(options) {
|
||||||
protected fun format(file: Path, contents: String): Pair<String, Int> {
|
protected fun format(file: Path, contents: String): Pair<String, Int> {
|
||||||
try {
|
try {
|
||||||
return Formatter().format(contents) to 0
|
return Formatter().format(contents, grammarVersion) to 0
|
||||||
} catch (pe: GenericParserError) {
|
} catch (pe: GenericParserError) {
|
||||||
consoleWriter.write("Could not format `$file`: $pe")
|
consoleWriter.write("Could not format `$file`: $pe")
|
||||||
consoleWriter.appendLine()
|
consoleWriter.appendLine()
|
||||||
|
|||||||
@@ -20,13 +20,17 @@ import com.github.ajalt.clikt.core.NoOpCliktCommand
|
|||||||
import com.github.ajalt.clikt.core.subcommands
|
import com.github.ajalt.clikt.core.subcommands
|
||||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||||
import com.github.ajalt.clikt.parameters.arguments.multiple
|
import com.github.ajalt.clikt.parameters.arguments.multiple
|
||||||
|
import com.github.ajalt.clikt.parameters.options.default
|
||||||
import com.github.ajalt.clikt.parameters.options.flag
|
import com.github.ajalt.clikt.parameters.options.flag
|
||||||
import com.github.ajalt.clikt.parameters.options.option
|
import com.github.ajalt.clikt.parameters.options.option
|
||||||
|
import com.github.ajalt.clikt.parameters.types.enum
|
||||||
import com.github.ajalt.clikt.parameters.types.path
|
import com.github.ajalt.clikt.parameters.types.path
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import org.pkl.cli.CliFormatterApply
|
import org.pkl.cli.CliFormatterApply
|
||||||
import org.pkl.cli.CliFormatterCheck
|
import org.pkl.cli.CliFormatterCheck
|
||||||
|
import org.pkl.cli.commands.FormatterCheckCommand.Companion.grammarVersionHelp
|
||||||
import org.pkl.commons.cli.commands.BaseCommand
|
import org.pkl.commons.cli.commands.BaseCommand
|
||||||
|
import org.pkl.formatter.GrammarVersion
|
||||||
|
|
||||||
class FormatterCommand : NoOpCliktCommand(name = "format") {
|
class FormatterCommand : NoOpCliktCommand(name = "format") {
|
||||||
override fun help(context: Context) = "Run commands related to formatting"
|
override fun help(context: Context) = "Run commands related to formatting"
|
||||||
@@ -47,8 +51,25 @@ class FormatterCheckCommand : BaseCommand(name = "check", helpLink = helpLink) {
|
|||||||
.path(mustExist = true, canBeDir = true)
|
.path(mustExist = true, canBeDir = true)
|
||||||
.multiple()
|
.multiple()
|
||||||
|
|
||||||
|
val grammarVersion: GrammarVersion by
|
||||||
|
option(names = arrayOf("--grammar-version"), help = grammarVersionHelp)
|
||||||
|
.enum<GrammarVersion> { "${it.version}" }
|
||||||
|
.default(GrammarVersion.latest())
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
CliFormatterCheck(baseOptions.baseOptions(emptyList()), paths).run()
|
CliFormatterCheck(baseOptions.baseOptions(emptyList()), paths, grammarVersion).run()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
internal val grammarVersionHelp =
|
||||||
|
"""
|
||||||
|
The grammar compatibility version to use.$NEWLINE
|
||||||
|
${GrammarVersion.entries.joinToString("$NEWLINE", prefix = " ") {
|
||||||
|
val default = if (it == GrammarVersion.latest()) " `(default)`" else ""
|
||||||
|
"`${it.version}`: ${it.versionSpan}$default"
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
.trimIndent()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +89,12 @@ class FormatterApplyCommand : BaseCommand(name = "apply", helpLink = helpLink) {
|
|||||||
)
|
)
|
||||||
.flag()
|
.flag()
|
||||||
|
|
||||||
|
val grammarVersion: GrammarVersion by
|
||||||
|
option(names = arrayOf("--grammar-version"), help = grammarVersionHelp)
|
||||||
|
.enum<GrammarVersion> { "${it.version}" }
|
||||||
|
.default(GrammarVersion.latest())
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
CliFormatterApply(baseOptions.baseOptions(emptyList()), paths, silent).run()
|
CliFormatterApply(baseOptions.baseOptions(emptyList()), paths, grammarVersion, silent).run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,8 +33,6 @@ import org.pkl.commons.cli.commands.BaseCommand
|
|||||||
import org.pkl.commons.cli.commands.TestOptions
|
import org.pkl.commons.cli.commands.TestOptions
|
||||||
import org.pkl.commons.cli.commands.single
|
import org.pkl.commons.cli.commands.single
|
||||||
|
|
||||||
private const val NEWLINE = '\u0085'
|
|
||||||
|
|
||||||
class ProjectCommand : NoOpCliktCommand(name = "project") {
|
class ProjectCommand : NoOpCliktCommand(name = "project") {
|
||||||
override fun help(context: Context) = "Run commands related to projects"
|
override fun help(context: Context) = "Run commands related to projects"
|
||||||
|
|
||||||
|
|||||||
18
pkl-cli/src/main/kotlin/org/pkl/cli/commands/Utils.kt
Normal file
18
pkl-cli/src/main/kotlin/org/pkl/cli/commands/Utils.kt
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
internal const val NEWLINE = '\u0085'
|
||||||
@@ -33,7 +33,7 @@ import org.pkl.parser.syntax.Operator
|
|||||||
import org.pkl.parser.syntax.generic.Node
|
import org.pkl.parser.syntax.generic.Node
|
||||||
import org.pkl.parser.syntax.generic.NodeType
|
import org.pkl.parser.syntax.generic.NodeType
|
||||||
|
|
||||||
internal class Builder(sourceText: String) {
|
internal class Builder(sourceText: String, private val grammarVersion: GrammarVersion) {
|
||||||
private var id: Int = 0
|
private var id: Int = 0
|
||||||
private val source: CharArray = sourceText.toCharArray()
|
private val source: CharArray = sourceText.toCharArray()
|
||||||
private var prevNode: Node? = null
|
private var prevNode: Node? = null
|
||||||
@@ -542,7 +542,11 @@ internal class Builder(sourceText: String) {
|
|||||||
if (prev.isTerminal("(") || next.isTerminal(")")) {
|
if (prev.isTerminal("(") || next.isTerminal(")")) {
|
||||||
if (next.isTerminal(")")) {
|
if (next.isTerminal(")")) {
|
||||||
// trailing comma
|
// trailing comma
|
||||||
ifWrap(groupId, nodes(Text(","), line()), line())
|
if (grammarVersion == GrammarVersion.V1) {
|
||||||
|
line()
|
||||||
|
} else {
|
||||||
|
ifWrap(groupId, nodes(Text(","), line()), line())
|
||||||
|
}
|
||||||
} else line()
|
} else line()
|
||||||
} else spaceOrLine()
|
} else spaceOrLine()
|
||||||
}
|
}
|
||||||
@@ -561,7 +565,11 @@ internal class Builder(sourceText: String) {
|
|||||||
val node = if (hasTrailingLambda) Empty else line()
|
val node = if (hasTrailingLambda) Empty else line()
|
||||||
if (next.isTerminal(")") && !hasTrailingLambda) {
|
if (next.isTerminal(")") && !hasTrailingLambda) {
|
||||||
// trailing comma
|
// trailing comma
|
||||||
ifWrap(groupId, nodes(Text(","), node), node)
|
if (grammarVersion == GrammarVersion.V1) {
|
||||||
|
node
|
||||||
|
} else {
|
||||||
|
ifWrap(groupId, nodes(Text(","), node), node)
|
||||||
|
}
|
||||||
} else node
|
} else node
|
||||||
} else spaceOrLine()
|
} else spaceOrLine()
|
||||||
},
|
},
|
||||||
@@ -664,7 +672,11 @@ internal class Builder(sourceText: String) {
|
|||||||
if (prev.isTerminal("<") || next.isTerminal(">")) {
|
if (prev.isTerminal("<") || next.isTerminal(">")) {
|
||||||
if (next.isTerminal(">")) {
|
if (next.isTerminal(">")) {
|
||||||
// trailing comma
|
// trailing comma
|
||||||
ifWrap(id, nodes(Text(","), line()), line())
|
if (grammarVersion == GrammarVersion.V1) {
|
||||||
|
Line
|
||||||
|
} else {
|
||||||
|
ifWrap(id, nodes(Text(","), line()), line())
|
||||||
|
}
|
||||||
} else line()
|
} else line()
|
||||||
} else spaceOrLine()
|
} else spaceOrLine()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,22 +27,24 @@ class Formatter {
|
|||||||
* Formats a Pkl file from the given file path.
|
* Formats a Pkl file from the given file path.
|
||||||
*
|
*
|
||||||
* @param path the path to the Pkl file to format
|
* @param path the path to the Pkl file to format
|
||||||
|
* @param grammarVersion grammar compatibility version
|
||||||
* @return the formatted Pkl source code as a string
|
* @return the formatted Pkl source code as a string
|
||||||
* @throws java.io.IOException if the file cannot be read
|
* @throws java.io.IOException if the file cannot be read
|
||||||
*/
|
*/
|
||||||
fun format(path: Path): String {
|
fun format(path: Path, grammarVersion: GrammarVersion = GrammarVersion.latest()): String {
|
||||||
return format(Files.readString(path))
|
return format(Files.readString(path), grammarVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats the given Pkl source code text.
|
* Formats the given Pkl source code text.
|
||||||
*
|
*
|
||||||
* @param text the Pkl source code to format
|
* @param text the Pkl source code to format
|
||||||
|
* @param grammarVersion grammar compatibility version
|
||||||
* @return the formatted Pkl source code as a string
|
* @return the formatted Pkl source code as a string
|
||||||
*/
|
*/
|
||||||
fun format(text: String): String {
|
fun format(text: String, grammarVersion: GrammarVersion = GrammarVersion.latest()): String {
|
||||||
val parser = GenericParser()
|
val parser = GenericParser()
|
||||||
val builder = Builder(text)
|
val builder = Builder(text, grammarVersion)
|
||||||
val gen = Generator()
|
val gen = Generator()
|
||||||
val ast = parser.parseModule(text)
|
val ast = parser.parseModule(text)
|
||||||
val formatAst = builder.format(ast)
|
val formatAst = builder.format(ast)
|
||||||
@@ -51,3 +53,13 @@ class Formatter {
|
|||||||
return gen.toString()
|
return gen.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Grammar compatibility version. */
|
||||||
|
enum class GrammarVersion(val version: Int, val versionSpan: String) {
|
||||||
|
V1(1, "0.25 - 0.29"),
|
||||||
|
V2(2, "0.30+");
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun latest(): GrammarVersion = entries.maxBy { it.version }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user