mirror of
https://github.com/apple/pkl.git
synced 2026-04-24 17:28:37 +02:00
Improve Formatter API (#1505)
- pass `GrammarVersion` to constructor instead of passing it to each `format` method - replace `format(Path): String` with `format(Reader, Appendable)` - instead of picking which overloads besides `format(String): String` might be useful, offer a single generalized method that streams input and output - add `@Throws(IOException::class)` to ensure that Java callers can catch this exception - deprecate old methods
This commit is contained in:
@@ -47,7 +47,7 @@ constructor(
|
|||||||
private val errWriter: Writer = System.err.writer(),
|
private val errWriter: Writer = System.err.writer(),
|
||||||
) : CliCommand(CliBaseOptions()) {
|
) : CliCommand(CliBaseOptions()) {
|
||||||
private fun format(contents: String): String {
|
private fun format(contents: String): String {
|
||||||
return Formatter().format(contents, grammarVersion)
|
return Formatter(grammarVersion).format(contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeErrLine(error: String) {
|
private fun writeErrLine(error: String) {
|
||||||
|
|||||||
@@ -15,14 +15,24 @@
|
|||||||
*/
|
*/
|
||||||
package org.pkl.formatter
|
package org.pkl.formatter
|
||||||
|
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.Reader
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
import kotlin.jvm.Throws
|
||||||
import org.pkl.formatter.ast.ForceLine
|
import org.pkl.formatter.ast.ForceLine
|
||||||
import org.pkl.formatter.ast.Nodes
|
import org.pkl.formatter.ast.Nodes
|
||||||
import org.pkl.parser.GenericParser
|
import org.pkl.parser.GenericParser
|
||||||
|
|
||||||
/** A formatter for Pkl files that applies canonical formatting rules. */
|
/**
|
||||||
class Formatter {
|
* A formatter for Pkl files that applies canonical formatting rules.
|
||||||
|
*
|
||||||
|
* @param grammarVersion grammar compatibility version
|
||||||
|
*/
|
||||||
|
class Formatter
|
||||||
|
@JvmOverloads
|
||||||
|
constructor(private val grammarVersion: GrammarVersion = GrammarVersion.latest()) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a Pkl file from the given file path.
|
* Formats a Pkl file from the given file path.
|
||||||
*
|
*
|
||||||
@@ -32,8 +42,9 @@ class Formatter {
|
|||||||
* @throws java.io.IOException if the file cannot be read
|
* @throws java.io.IOException if the file cannot be read
|
||||||
*/
|
*/
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
|
@Deprecated(message = "use format(path.readText()) instead")
|
||||||
fun format(path: Path, grammarVersion: GrammarVersion = GrammarVersion.latest()): String {
|
fun format(path: Path, grammarVersion: GrammarVersion = GrammarVersion.latest()): String {
|
||||||
return format(Files.readString(path), grammarVersion)
|
return Formatter(grammarVersion).format(Files.readString(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -43,16 +54,41 @@ class Formatter {
|
|||||||
* @param grammarVersion grammar compatibility version
|
* @param grammarVersion grammar compatibility version
|
||||||
* @return the formatted Pkl source code as a string
|
* @return the formatted Pkl source code as a string
|
||||||
*/
|
*/
|
||||||
@JvmOverloads
|
@Deprecated(message = "use Formatter(grammarVersion).format(text) instead")
|
||||||
fun format(text: String, grammarVersion: GrammarVersion = GrammarVersion.latest()): String {
|
fun format(text: String, grammarVersion: GrammarVersion): String {
|
||||||
val parser = GenericParser()
|
return Formatter(grammarVersion).format(text)
|
||||||
val builder = Builder(text, grammarVersion)
|
}
|
||||||
val gen = Generator()
|
|
||||||
val ast = parser.parseModule(text)
|
/**
|
||||||
val formatAst = builder.format(ast)
|
* Formats the given Pkl source code text.
|
||||||
|
*
|
||||||
|
* @param text the Pkl source code to format
|
||||||
|
* @return the formatted Pkl source code as a string
|
||||||
|
*/
|
||||||
|
fun format(text: String): String {
|
||||||
|
return buildString { format(text, this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the given Pkl source code text.
|
||||||
|
*
|
||||||
|
* It is the caller's responsibility to close [input], and, if applicable, [output].
|
||||||
|
*
|
||||||
|
* @param input the Pkl source code to format
|
||||||
|
* @param output the formatted Pkl source code
|
||||||
|
* @throws java.io.IOException if an I/O error occurs during reading or writing
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun format(input: Reader, output: Appendable) {
|
||||||
|
format(input.readText(), output)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun format(input: String, output: Appendable) {
|
||||||
|
val ast = GenericParser().parseModule(input)
|
||||||
|
val formatAst = Builder(input, grammarVersion).format(ast)
|
||||||
// force a line at the end of the file
|
// force a line at the end of the file
|
||||||
gen.generate(Nodes(listOf(formatAst, ForceLine)))
|
val nodes = Nodes(listOf(formatAst, ForceLine))
|
||||||
return gen.toString()
|
Generator(output).generate(nodes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2025-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -29,8 +29,7 @@ import org.pkl.formatter.ast.SpaceOrLine
|
|||||||
import org.pkl.formatter.ast.Text
|
import org.pkl.formatter.ast.Text
|
||||||
import org.pkl.formatter.ast.Wrap
|
import org.pkl.formatter.ast.Wrap
|
||||||
|
|
||||||
internal class Generator {
|
internal class Generator(private val buf: Appendable) {
|
||||||
private val buf: StringBuilder = StringBuilder()
|
|
||||||
private var indent: Int = 0
|
private var indent: Int = 0
|
||||||
private var size: Int = 0
|
private var size: Int = 0
|
||||||
private val wrapped: MutableSet<Int> = mutableSetOf()
|
private val wrapped: MutableSet<Int> = mutableSetOf()
|
||||||
@@ -138,10 +137,6 @@ internal class Generator {
|
|||||||
return if (beforeLast is Text) beforeLast.text.length else 0
|
return if (beforeLast is Text) beforeLast.text.length else 0
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return buf.toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// max line length
|
// max line length
|
||||||
const val MAX = 100
|
const val MAX = 100
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2025-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,6 +17,7 @@ package org.pkl.formatter
|
|||||||
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.io.path.isRegularFile
|
import kotlin.io.path.isRegularFile
|
||||||
|
import kotlin.io.path.readText
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import org.pkl.commons.test.InputOutputTestEngine
|
import org.pkl.commons.test.InputOutputTestEngine
|
||||||
import org.pkl.parser.ParserError
|
import org.pkl.parser.ParserError
|
||||||
@@ -58,16 +59,10 @@ abstract class AbstractFormatterSnippetTestsEngine : InputOutputTestEngine() {
|
|||||||
class FormatterSnippetTestsEngine : AbstractFormatterSnippetTestsEngine() {
|
class FormatterSnippetTestsEngine : AbstractFormatterSnippetTestsEngine() {
|
||||||
override val testClass: KClass<*> = FormatterSnippetTests::class
|
override val testClass: KClass<*> = FormatterSnippetTests::class
|
||||||
|
|
||||||
override fun generateOutputFor(inputFile: Path): Pair<Boolean, String> {
|
override fun generateOutputFor(inputFile: Path): Pair<Boolean, String> =
|
||||||
val formatter = Formatter()
|
try {
|
||||||
val (success, output) =
|
true to Formatter().format(inputFile.readText())
|
||||||
try {
|
} catch (_: ParserError) {
|
||||||
val res = formatter.format(inputFile)
|
false to ""
|
||||||
true to res
|
}
|
||||||
} catch (_: ParserError) {
|
|
||||||
false to ""
|
|
||||||
}
|
|
||||||
|
|
||||||
return success to output
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2025-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -120,4 +120,12 @@ class FormatterTest {
|
|||||||
assertThat(format(src)).isEqualTo("\n")
|
assertThat(format(src)).isEqualTo("\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `read from Reader and write to Appendable`() {
|
||||||
|
val input = " x = 42".reader()
|
||||||
|
val output = StringBuilder()
|
||||||
|
Formatter().format(input, output)
|
||||||
|
assertThat(output.toString()).isEqualTo("x = 42\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user