CodePart is the fundamental building block of code generation in CodeGentle. Each CodePart represents a specific element of source code, from simple text to complex structural elements like indentation and control flow.
sealed class CodePart
├── CodeSimplePart
└── CodeArgumentPart (sealed)
├── Skip
├── Literal
├── Name
├── Str
├── Type
├── TypeRef
├── Indent
├── Unindent
├── StatementBegin
├── StatementEnd
├── WrappingSpace
├── ZeroWidthSpace
├── Newline
├── ControlFlow
└── OtherCodeValueRepresents plain text without any special processing.
CodePart.simple(value: String): CodeSimplePartval part = CodePart.simple("println(\"Hello\")")
// Outputs exactly: println("Hello")Used internally when creating CodeValue instances without placeholders:
CodeValue("val x = 10")
// Internally creates: CodeSimplePart("val x = 10")Abstract base for all code parts that can be used as placeholder arguments. These are the parts you pass to replace %V placeholders in format strings.
Outputs the literal placeholder string %V without replacement.
CodePart.skip(): CodeArgumentPartCodeValue("The placeholder is %V", CodePart.skip())
// Results in: The placeholder is %VEmits a value as-is, without quotes or escaping. Use for numbers, boolean values, constants, and expressions.
CodePart.literal(value: Any?): CodeArgumentPart// Numbers
CodeValue("val count = %V", CodePart.literal(42))
// Results in: val count = 42
// Booleans
CodeValue("val isActive = %V", CodePart.literal(true))
// Results in: val isActive = true
// Constants/expressions
CodeValue("val max = %V", CodePart.literal("Integer.MAX_VALUE"))
// Results in: val max = Integer.MAX_VALUE
// Null
CodeValue("val optional = %V", CodePart.literal(null))
// Results in: val optional = nullEmits a name (variable, function, class, etc.). Accepts String, CharSequence, or any object implementing the Named interface.
CodePart.name(nameValue: Any?): CodeArgumentPart// Simple string name
CodeValue("val %V = 10", CodePart.name("counter"))
// Results in: val counter = 10
// Function call
CodeValue("%V()", CodePart.name("calculateTotal"))
// Results in: calculateTotal()
// Named object
val parameter = SomeNamedObject(name = "userId")
CodeValue("val %V: Int", CodePart.name(parameter))
// Results in: val userId: IntEmits a string value wrapped in double quotes with proper escaping. Use for string literals.
CodePart.string(value: String?): CodeArgumentPart
CodePart.string(value: String?, handleSpecialCharacter: Boolean): CodeArgumentPartvalue: The string value to emithandleSpecialCharacter: Iftrue(default), escapes special characters like$in Kotlin
// Basic string
CodeValue("val message = %V", CodePart.string("Hello, World"))
// Results in: val message = "Hello, World"
// String with quotes
CodeValue("val quote = %V", CodePart.string("He said \"Hi\""))
// Results in: val quote = "He said \"Hi\""
// String with special characters
CodeValue("val template = %V", CodePart.string("Price: $100"))
// Results in Kotlin: val template = "Price: \$100"
// Null string
CodeValue("val empty = %V", CodePart.string(null))
// Results in: val empty = nullWhen handleSpecialCharacter is true:
- Kotlin: Escapes
$to\$to prevent string interpolation - Java: No special handling (double quotes are always escaped)
// With special character handling (default)
CodePart.string("Total: $amount", handleSpecialCharacter = true)
// Kotlin: "Total: \$amount"
// Without special character handling
CodePart.string("Total: $amount", handleSpecialCharacter = false)
// Kotlin: "Total: $amount" (may cause issues if $amount is not a variable)Emits a type reference using a TypeName object.
CodePart.type(type: TypeName): CodeArgumentPart// Simple type
CodeValue("val count: %V", CodePart.type(IntTypeName))
// Results in: val count: Int
// Generic type
val listType = TypeName("List", listOf(StringTypeName))
CodeValue("val items: %V", CodePart.type(listType))
// Results in: val items: List<String>
// Function parameter
CodeValue("fun process(data: %V)", CodePart.type(DataTypeName))
// Results in: fun process(data: Data)Emits a type reference using a TypeRef<*> object. TypeRef is a runtime type reference that can capture generic information.
CodePart.type(type: TypeRef<*>): CodeArgumentPart// Using TypeRef
val typeRef = typeRef<List<String>>()
CodeValue("val items: %V", CodePart.type(typeRef))
// Results in: val items: List<String>
// Complex generic type
val complexRef = typeRef<Map<String, List<Int>>>()
CodeValue("val data: %V", CodePart.type(complexRef))
// Results in: val data: Map<String, List<Int>>Increases the indentation level. Each indent adds one level of indentation (typically 4 spaces or 1 tab, depending on the writer configuration).
CodePart.indent(levels: Int = 1): CodeArgumentPartCodeValue {
addCode("class Person {")
addCode("%V", CodePart.indent())
addCode("val name: String")
addCode("%V", CodePart.unindent())
addCode("}")
}
// Results in:
// class Person {
// val name: String
// }
// Multiple levels
CodeValue {
addCode("class Outer {")
addCode("%V", CodePart.indent(2))
addCode("val nested: String")
addCode("%V", CodePart.unindent(2))
addCode("}")
}Typically used through CodeValueBuilder.indent() for cleaner code:
CodeValue {
addCode("class Person {")
indent()
addCode("val name: String")
unindent()
addCode("}")
}Decreases the indentation level.
CodePart.unindent(levels: Int = 1): CodeArgumentPartCodeValue {
addCode("if (condition) {")
addCode("%V", CodePart.indent())
addCode("doSomething()")
addCode("%V", CodePart.unindent())
addCode("}")
}Use CodeValueBuilder.unindent() for cleaner syntax.
Marks the beginning of a statement. Used for multi-line statement formatting where continuation lines need double indentation.
CodePart.statementBegin(): CodeArgumentPartTypically used internally by addStatement():
// Manually
CodeValue {
addCode("%V", CodePart.statementBegin())
addCode("val result = someLongFunction(")
indent()
addCode("parameter1,")
addCode("parameter2")
unindent()
addCode(")")
addCode("%V", CodePart.statementEnd())
}
// Better: use addStatement()
CodeValue {
addStatement("val x = %V", CodePart.literal(10))
}Marks the end of a statement.
CodePart.statementEnd(): CodeArgumentPartSee StatementBegin above. Generally used through addStatement() rather than manually.
Emits a space or newline depending on the current line position. Prefers to wrap lines before reaching a certain column limit (typically 100 columns).
CodePart.wrappingSpace(): CodeArgumentPartCodeValue {
addCode("fun veryLongFunctionName(")
addCode("parameter1: String,%V", CodePart.wrappingSpace())
addCode("parameter2: Int,%V", CodePart.wrappingSpace())
addCode("parameter3: Boolean")
addCode(")")
}
// If the line is short: fun veryLongFunctionName(parameter1: String, parameter2: Int, parameter3: Boolean)
// If the line is too long:
// fun veryLongFunctionName(parameter1: String,
// parameter2: Int,
// parameter3: Boolean)Acts as a zero-width space that allows line wrapping at this position when lines exceed the column limit, but doesn't add any space.
CodePart.zeroWidthSpace(): CodeArgumentPartCodeValue {
addCode("veryLongClassName%V.%VveryLongMethodName()",
CodePart.zeroWidthSpace(),
CodePart.zeroWidthSpace()
)
}
// If line is short: veryLongClassName.veryLongMethodName()
// If line is too long:
// veryLongClassName.
// veryLongMethodName()Wrapping Space - Emits a space or newline depending on line position, preferring wrapping before 100 columns.
CodePart.wrappingSpace(): CodeArgumentPart// In a long method call
CodeValue("someMethod(%V,%V%V,%V)",
CodePart.literal(arg1),
CodePart.wrappingSpace(), // Will wrap if line > 100 cols
CodePart.literal(arg2),
CodePart.literal(arg3)
)
// Results in either:
// someMethod(arg1, arg2, arg3)
// or (if long):
// someMethod(arg1,
// arg2, arg3)Behavior:
- Defers decision until flush
- Uses current indentation + 2 levels when wrapping
- Column limit: 100 characters (default)
Zero-Width Space - Acts as an optional line wrap point without emitting any character if not wrapped.
CodePart.zeroWidthSpace(): CodeArgumentPart// For parameter lists that should break at specific points
CodeValue("foo(%Zarg1,%Zarg2,%Zarg3)",
CodePart.zeroWidthSpace(),
CodePart.literal("value1"),
CodePart.zeroWidthSpace(),
CodePart.literal("value2"),
CodePart.zeroWidthSpace(),
CodePart.literal("value3")
)
// Either: foo(arg1, arg2, arg3)
// Or wrapped: foo(
// arg1,
// arg2,
// arg3
// )Behavior:
- Emits nothing if line stays within limit
- Emits newline + indentation if wrapping needed
- Useful for optional breaking points
Emits an explicit newline character.
CodePart.newline(): CodeArgumentPartCodeValue("First line%VSecond line", CodePart.newline())
// Results in:
// First line
// Second line
// Multiple newlines for blank lines
CodeValue("Section 1%V%VSection 2",
CodePart.newline(),
CodePart.newline()
)
// Results in:
// Section 1
//
// Section 2Represents control flow structures like if/else, try/catch, loops, etc. This is a complex part that manages control flow block boundaries.
enum class Position {
BEGIN, // Start of a control flow block
NEXT, // Continuation (else, catch, etc.)
END // End of a control flow block
}CodePart.beginControlFlow(): CodeArgumentPart
CodePart.beginControlFlow(codeValue: CodeValue): CodeArgumentPart
CodePart.beginControlFlow(format: String, vararg arguments: CodeArgumentPart): CodeArgumentPart
CodePart.beginControlFlow(format: String, block: CodeValueSingleFormatBuilderDsl): CodeArgumentPartCodePart.nextControlFlow(): CodeArgumentPart
CodePart.nextControlFlow(codeValue: CodeValue): CodeArgumentPart
CodePart.nextControlFlow(format: String, vararg arguments: CodeArgumentPart): CodeArgumentPart
CodePart.nextControlFlow(format: String, block: CodeValueSingleFormatBuilderDsl): CodeArgumentPartCodePart.endControlFlow(): CodeArgumentPart
CodePart.endControlFlow(codeValue: CodeValue): CodeArgumentPart
CodePart.endControlFlow(format: String, vararg arguments: CodeArgumentPart): CodeArgumentPart
CodePart.endControlFlow(format: String, block: CodeValueSingleFormatBuilderDsl): CodeArgumentPartCodeValue {
addCode("%V", CodePart.beginControlFlow("if (x > 0)"))
addCode("println(\"positive\")")
addCode("%V", CodePart.endControlFlow())
}
// Results in:
// if (x > 0) {
// println("positive")
// }CodeValue {
addCode("%V", CodePart.beginControlFlow("if (x > 0)"))
addCode("println(\"positive\")")
addCode("%V", CodePart.nextControlFlow("else"))
addCode("println(\"not positive\")")
addCode("%V", CodePart.endControlFlow())
}
// Results in:
// if (x > 0) {
// println("positive")
// } else {
// println("not positive")
// }CodeValue {
addCode("%V", CodePart.beginControlFlow("try"))
addCode("riskyOperation()")
addCode("%V", CodePart.nextControlFlow("catch (Exception e)"))
addCode("handleError()")
addCode("%V", CodePart.nextControlFlow("finally"))
addCode("cleanup()")
addCode("%V", CodePart.endControlFlow())
}
// Results in:
// try {
// riskyOperation()
// } catch (Exception e) {
// handleError()
// } finally {
// cleanup()
// }CodeValue {
addCode("%V", CodePart.beginControlFlow("do"))
addCode("processItem()")
addCode("count++")
addCode("%V", CodePart.endControlFlow("while (count < 10)"))
}
// Results in (Java):
// do {
// processItem();
// count++;
// } while (count < 10);CodeValue {
addCode("%V", CodePart.beginControlFlow("if (%V > %V)",
CodePart.name("count"),
CodePart.literal(0)
))
addCode("process()")
addCode("%V", CodePart.endControlFlow())
}
// Results in:
// if (count > 0) {
// process()
// }Instead of using CodePart.beginControlFlow() directly, use the builder methods:
CodeValue {
beginControlFlow("if (x > 0)") // Instead of addCode with CodePart.beginControlFlow
addCode("println(\"positive\")")
endControlFlow()
}Embeds another CodeValue as a part. This allows nesting and composition of code values.
CodePart.otherCodeValue(value: CodeValue): CodeArgumentPart// Create a reusable code value
val condition = CodeValue("x > 0 && y < 10")
// Use it in another code value
CodeValue("if (%V)", CodePart.otherCodeValue(condition))
// Results in: if (x > 0 && y < 10)
// Complex example
val parameters = CodeValue {
addCode("name: String,")
addCode("age: Int,")
addCode("email: String")
}
val functionDecl = CodeValue("fun createUser(%V)",
CodePart.otherCodeValue(parameters)
)
// Results in: fun createUser(name: String, age: Int, email: String)// Simple declaration
CodeValue("val %V = %V",
CodePart.name("count"),
CodePart.literal(0)
)
// Results in: val count = 0
// With type
CodeValue("val %V: %V = %V",
CodePart.name("message"),
CodePart.type(StringTypeName),
CodePart.string("Hello")
)
// Results in: val message: String = "Hello"CodeValue("fun %V(%V: %V, %V: %V): %V",
CodePart.name("calculate"),
CodePart.name("x"),
CodePart.type(IntTypeName),
CodePart.name("y"),
CodePart.type(IntTypeName),
CodePart.type(IntTypeName)
)
// Results in: fun calculate(x: Int, y: Int): IntCodeValue {
addCode("class %V {", CodePart.name("Person"))
indent()
addStatement("val %V: %V",
CodePart.name("name"),
CodePart.type(StringTypeName)
)
addStatement("val %V: %V",
CodePart.name("age"),
CodePart.type(IntTypeName)
)
unindent()
addCode("}")
}
// Results in:
// class Person {
// val name: String
// val age: Int
// }CodeValue {
beginControlFlow("if (%V)", CodePart.name("isValid"))
addStatement("process()")
nextControlFlow("else")
addStatement("reject()")
endControlFlow()
}
// Results in:
// if (isValid) {
// process()
// } else {
// reject()
// }CodeValue {
beginControlFlow("try")
addStatement("val result = fetchData()")
addStatement("return result")
nextControlFlow("catch (%V e)", CodePart.type(IOExceptionTypeName))
addStatement("logger.error(%V, e)", CodePart.string("Failed to fetch"))
addStatement("throw RuntimeException(e)")
endControlFlow()
}
// Results in:
// try {
// val result = fetchData()
// return result
// } catch (IOException e) {
// logger.error("Failed to fetch", e)
// throw RuntimeException(e)
// }CodeValue {
beginControlFlow("for (%V in %V)",
CodePart.name("item"),
CodePart.name("items")
)
addStatement("process(%V)", CodePart.name("item"))
endControlFlow()
}
// Results in:
// for (item in items) {
// process(item)
// }| Use Case | CodePart Type | Example |
|---|---|---|
| Plain text | simple() |
CodePart.simple("public") |
| Numbers, booleans | literal() |
CodePart.literal(42) |
| Constants, expressions | literal() |
CodePart.literal("MAX_VALUE") |
| Variable/function names | name() |
CodePart.name("userName") |
| String literals | string() |
CodePart.string("Hello") |
| Type references | type() |
CodePart.type(StringTypeName) |
| Increase indent | indent() |
CodePart.indent() |
| Decrease indent | unindent() |
CodePart.unindent() |
| Control structures | beginControlFlow() etc. |
CodePart.beginControlFlow("if (x)") |
| Nested code | otherCodeValue() |
CodePart.otherCodeValue(code) |
| Line break | newline() |
CodePart.newline() |
| Smart wrapping | wrappingSpace() |
CodePart.wrappingSpace() |
| Skip placeholder | skip() |
CodePart.skip() |
-
Use Appropriate Types: Choose the correct
CodeParttype for each element. Don't useliteral()for strings that need quotes. -
Leverage Builder Methods: Use
CodeValueBuildermethods likeindent(),beginControlFlow()instead of manually addingCodePartinstances. -
Manage Indentation: Always pair
indent()withunindent(). -
Use Statement Markers: Use
addStatement()for statements to get proper formatting. -
Compose with OtherCodeValue: Break complex code into smaller
CodeValuepieces and compose them withotherCodeValue(). -
Handle Special Characters: Use
string()withhandleSpecialCharacter = true(default) for Kotlin string templates. -
Smart Line Breaks: Use
wrappingSpace()for flexible formatting that adapts to line length.