CodeValue is the core interface for representing code fragments in CodeGentle. It encapsulates a list of CodePart instances that together form a complete piece of code.
interface CodeValue {
val parts: List<CodePart>
}A CodeValue is essentially a container for a sequence of code parts that will be emitted as source code.
CodeValue uses %V as a universal placeholder for dynamic content insertion. When you write a format string, each %V is replaced by the corresponding CodeArgumentPart you provide.
// Single placeholder
CodeValue("val name = %V", CodePart.string("Alice"))
// Results in: val name = "Alice"
// Multiple placeholders
CodeValue("val %V: %V = %V",
CodePart.name("count"),
CodePart.type(IntTypeName),
CodePart.literal(42)
)
// Results in: val count: Int = 42If you need to output the literal string %V, use CodePart.skip():
CodeValue("The placeholder is %V", CodePart.skip())
// Results in: The placeholder is %Vval empty = CodeValue()Creates an empty CodeValue with no parts.
val code = CodeValue(listOf(
CodePart.simple("val x = "),
CodePart.literal(10)
))Creates a CodeValue from an explicit list of parts.
// Without placeholders
val simple = CodeValue("println(\"Hello World\")")
// With single placeholder
val withArg = CodeValue("val x = %V", CodePart.literal(42))
// With multiple placeholders
val multi = CodeValue(
"fun %V(%V: %V): %V",
CodePart.name("greet"),
CodePart.name("name"),
CodePart.type(StringTypeName),
CodePart.type(UnitTypeName)
)val code = CodeValue("val %V = %V") {
addValue(CodePart.name("result"))
addValue(CodePart.literal(100))
}
// Results in: val result = 100For complex code construction:
val code = CodeValue {
addCode("class Person {")
indent()
addStatement("val name: String")
addStatement("val age: Int")
unindent()
addCode("}")
}Optimized builder for working with a single format string containing placeholders.
val builder = CodeValue.builder("%V %V = %V;")val code = CodeValue.builder("%V %V = %V;")
.addValue(CodePart.type(IntTypeName))
.addValue(CodePart.name("count"))
.addValue(CodePart.literal(0))
.build()
// Results in: int count = 0;// Using vararg
builder.addValues(
CodePart.type(StringTypeName),
CodePart.name("message"),
CodePart.string("Hello")
)
// Using Iterable
val parts = listOf(
CodePart.name("x"),
CodePart.literal(10)
)
builder.addValues(parts)val code = CodeValue("%V %V = %V") {
addValue(CodePart.type(StringTypeName))
addValue(CodePart.name("greeting"))
addValue(CodePart.string("Hello"))
}General-purpose builder for constructing complex code structures.
val builder = CodeValue.builder()Adds another CodeValue to the builder:
val builder = CodeValue.builder()
builder.addCode(CodeValue("val x = 10"))
builder.addCode(CodeValue("val y = 20"))Adds a simple format string without placeholders:
builder.addCode("println(\"Hello\")")Adds code with placeholders and arguments:
builder.addCode("val %V = %V",
CodePart.name("count"),
CodePart.literal(0)
)Adds code with a builder block for arguments:
builder.addCode("fun %V(): %V") {
addValue(CodePart.name("getData"))
addValue(CodePart.type(StringTypeName))
}Adds a statement with automatic StatementBegin and StatementEnd markers. These markers help with proper formatting and semicolon placement:
builder.addStatement("val x = %V", CodePart.literal(10))
// Wrapped with statement markers for proper formattingAdds a CodeValue as a statement:
val assignment = CodeValue("x = y + z")
builder.addStatement(assignment)Increases the indentation level by one:
builder.addCode("class Person {")
builder.indent()
builder.addCode("val name: String")
builder.unindent()
builder.addCode("}")Decreases the indentation level by one:
builder.unindent()Removes all parts from the builder:
builder.addCode("val x = 10")
builder.clear() // Builder is now emptyConstructs the final CodeValue:
val code = builder.build()CodeValue provides extensive support for control flow structures. See Control Flow examples below.
CodeValue {
beginControlFlow("if (x > 0)")
addCode("println(\"positive\")")
endControlFlow()
}CodeValue {
beginControlFlow("if (%V > %V)",
CodePart.name("x"),
CodePart.literal(0)
)
addCode("println(\"positive\")")
endControlFlow()
}CodeValue {
ifControlFlow("x > 0")
addCode("println(\"positive\")")
elseIfControlFlow("x < 0")
addCode("println(\"negative\")")
elseControlFlow()
addCode("println(\"zero\")")
endControlFlow()
}CodeValue {
tryControlFlow()
addCode("riskyOperation()")
catchControlFlow("IOException e")
addCode("handleError()")
finallyControlFlow()
addCode("cleanup()")
endControlFlow()
}CodeValue {
whileControlFlow("i < 10")
addCode("process(i)")
addCode("i++")
endControlFlow()
}CodeValue {
doControlFlow()
addCode("process(item)")
addCode("count++")
doWhileEndControlFlow("count < 10")
}// Java-style
CodeValue {
forControlFlow("int i = 0; i < 10; i++")
addCode("process(i)")
endControlFlow()
}
// Kotlin-style
CodeValue {
forControlFlow("i in 0 until 10")
addCode("process(i)")
endControlFlow()
}Creates a complete control flow block in one call:
CodeValue {
inControlFlow("if (x > %V)", CodePart.literal(0)) {
addCode("println(\"positive\")")
}
}
// Automatically begins and ends the control flowCombines two CodeValue instances:
val code1 = CodeValue("val x = 10")
val code2 = CodeValue("val y = 20")
val combined = code1 + code2
// Combined contains both partsChecks if a CodeValue has no parts:
val empty = CodeValue()
if (empty.isEmpty()) {
println("No code")
}Checks if a CodeValue has parts:
val code = CodeValue("val x = 10")
if (code.isNotEmpty()) {
println("Has code")
}val classCode = CodeValue {
addCode("class User(")
indent()
addStatement("val name: String")
addStatement("val email: String")
unindent()
addCode(") {")
indent()
// Add a method
addCode("fun greet(): String {")
indent()
addStatement("return %V", CodePart.string("Hello, \$name"))
unindent()
addCode("}")
unindent()
addCode("}")
}val functionCode = CodeValue {
addCode("fun loadData(id: %V): %V {",
CodePart.type(IntTypeName),
CodePart.type(DataTypeName)
)
indent()
tryControlFlow()
addStatement("val result = database.query(id)")
addStatement("return result")
catchControlFlow("SQLException e")
addStatement("logger.error(%V, e)",
CodePart.string("Failed to load data")
)
addStatement("throw RuntimeException(e)")
finallyControlFlow()
addStatement("database.close()")
endControlFlow()
unindent()
addCode("}")
}val conditionalCode = CodeValue {
ifControlFlow("status == %V", CodePart.literal("ACTIVE")) {
addValue(CodePart.name("Status.ACTIVE"))
}
addStatement("processActive()")
elseIfControlFlow("status == %V", CodePart.literal("PENDING")) {
addValue(CodePart.name("Status.PENDING"))
}
addStatement("processPending()")
elseControlFlow()
addStatement("processInactive()")
endControlFlow()
}val loopCode = CodeValue {
addStatement("val items = getItems()")
forControlFlow("item in items")
ifControlFlow("item.isValid()")
addStatement("process(item)")
elseControlFlow()
addStatement("skip(item)")
endControlFlow()
endControlFlow()
}-
Use Appropriate Builders: Use
CodeValueSingleFormatBuilderfor simple format strings,CodeValueBuilderfor complex structures. -
Leverage Control Flow Helpers: Use
ifControlFlow,tryControlFlow, etc., instead of manually building control structures. -
Manage Indentation: Always pair
indent()withunindent()to maintain proper code structure. -
Use Statement Markers: Use
addStatement()instead ofaddCode()for statements that need proper formatting. -
Choose Correct Part Types: Use appropriate
CodeParttypes (literal,string,name,type) for different code elements. -
Combine Values: Use the
+operator to combine simpleCodeValueinstances. -
Builder Pattern: Use builder DSL for readable, maintainable code construction.