Closed
Description
openedon Feb 4, 2020
The issue I have is positional arguments are sometimes hard to understand and maintain.
On the other hand, Kotlin has string templates, so why don't we use it for templating purposes?
For instance:
val hello = """Hello, "world", \n test"""
val j = FieldSpec.builder(Int::class.typeName, "j").build()
val codeBlock = codeBlock { "println(${hello.S} + ${j.N})" }
println(codeBlock.toString())
==>
println("Hello, \"world\", \\n test" + j)
One of my use cases:
codeBlock {
"""
${config.tokenTypeName.T} oldToken = ${token.N};
${config.tokenTypeName.T} nextToken = oldToken.next;
if (nextToken == null) {
nextToken = ${token_source.N}.${lexer.generateMethodName.N}(kind);
${jj_tokenCount.N}++;
if (${tokenOutput.N} != null) {
${tokenOutput.N}.accept(nextToken);
}
}
${token.N} = nextToken;
oldToken.next = null; // Stop nepotism
return nextToken;
""".trimIndent()
}
Here's my implementation (it is for JavaPoet's API, however, it should be more or less the same for KotlinPoet):
typealias StringTemplateMethod = StringTemplate.() -> String
class StringTemplate {
val args = mutableListOf<Any?>()
private fun arg(mask: String, v: Any?) = args.run {
add(v)
"$$size$mask"
}
val TypeName.T: String get() = arg("T", this)
val FieldSpec.T: String get() = arg("T", this.type)
val MethodSpec.T: String get() = arg("T", this.returnType)
val FieldSpec.N: String get() = arg("N", this)
val MethodSpec.N: String get() = arg("N", this)
val TypeSpec.N: String get() = arg("N", this)
val String.N: String get() = arg("N", this)
val Any?.L: String get() = arg("L", this)
val Any?.S: String get() = arg("S", this)
}
inline fun CodeBlock.Builder.add(template: StringTemplate.() -> String) = run {
val t = StringTemplate()
val format = t.template()
add(format, *t.args.toTypedArray())!!
}
inline fun codeBlock(template: StringTemplate.() -> String) =
CodeBlock.builder().add(template).build()!!
It might be convenient to have methods like beginControlFlow(template: StringTemplateMethod)
Basically every time there's (String, vararg args)
method, it might be better written as (StringTemplateMethod)
For instance, the following sample from readme
.addStatement("if (ingredient is %T) taco %M ingredient", meat, minusAssign)
becomes
.addStatement { "if (ingredient is ${meat.T}) taco ${minusAssign.M} ingredient" }
See square/javapoet#761 (comment)
See tieskedh/KotlinPoetDSL#55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Metadata
Assignees
Labels
No labels