CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-squareup--kotlinpoet

Kotlin and Java API for generating Kotlin source files (.kt) with type-safe, fluent builder patterns

Pending
Overview
Eval results
Files

code-generation.mddocs/

Code Generation and Templates

Template-based code generation with placeholder substitution, supporting complex code structures and maintaining proper formatting and imports.

Capabilities

Code Block Generation

Creates formatted code fragments with placeholder substitution and proper indentation, forming the building blocks for all code generation in KotlinPoet.

/**
 * A fragment of code with placeholder substitution
 */
class CodeBlock private constructor() {
    /** Whether this code block is empty */
    val isEmpty: Boolean
    
    /** Whether this code block is not empty */
    val isNotEmpty: Boolean
    
    companion object {
        /** Creates a code block from a format string and arguments */
        fun of(format: String, vararg args: Any?): CodeBlock
        
        /** Creates an empty code block */
        fun builder(): Builder
        
        /** Joins multiple code blocks with a separator */
        fun join(codeBlocks: Iterable<CodeBlock>, separator: String = ", "): CodeBlock
        fun join(codeBlocks: Iterable<CodeBlock>, separator: CodeBlock): CodeBlock
        
        /** Creates a code block that joins with newlines */
        fun joining(separator: String = ", "): Collector<CodeBlock, *, CodeBlock>
        fun joining(separator: CodeBlock): Collector<CodeBlock, *, CodeBlock>
    }
    
    /** Builder for constructing CodeBlock instances */
    class Builder {
        /** Adds formatted code to the block */
        fun add(format: String, vararg args: Any?): Builder
        
        /** Adds named code with a parameter map */
        fun addNamed(format: String, args: Map<String, *>): Builder
        
        /** Adds a code statement (automatically adds newline) */
        fun addStatement(format: String, vararg args: Any?): Builder
        
        /** Adds raw code without formatting */
        fun add(codeBlock: CodeBlock): Builder
        
        /** Begins a control flow block (if, for, while, etc.) */
        fun beginControlFlow(controlFlow: String, vararg args: Any?): Builder
        
        /** Adds to the current control flow */
        fun nextControlFlow(controlFlow: String, vararg args: Any?): Builder
        
        /** Ends the current control flow block */
        fun endControlFlow(): Builder
        fun endControlFlow(controlFlow: String, vararg args: Any?): Builder
        
        /** Increases indentation level */
        fun indent(): Builder
        
        /** Decreases indentation level */
        fun unindent(): Builder
        
        /** Clears all content from the builder */
        fun clear(): Builder
        
        /** Builds the CodeBlock */
        fun build(): CodeBlock
    }
}

Usage Examples:

import com.squareup.kotlinpoet.*

// Simple code block with placeholders
val simpleCode = CodeBlock.of("println(%S)", "Hello, World!")

// Code block with multiple statements
val multiStatementCode = CodeBlock.builder()
    .addStatement("val name = %S", "Alice")
    .addStatement("val age = %L", 25)
    .addStatement("println(%S + name + %S + age)", "Name: ", ", Age: ")
    .build()

// Control flow structures
val ifElseCode = CodeBlock.builder()
    .beginControlFlow("if (age >= 18)")
    .addStatement("println(%S)", "Adult")
    .nextControlFlow("else")
    .addStatement("println(%S)", "Minor")
    .endControlFlow()
    .build()

// For loop
val forLoopCode = CodeBlock.builder()
    .beginControlFlow("for (i in 1..10)")
    .addStatement("println(i)")
    .endControlFlow()
    .build()

// When expression
val whenCode = CodeBlock.builder()
    .beginControlFlow("when (status)")
    .addStatement("%T.PENDING -> %S", statusEnum, "Waiting for approval")
    .addStatement("%T.APPROVED -> %S", statusEnum, "Request approved")
    .addStatement("%T.REJECTED -> %S", statusEnum, "Request rejected")
    .addStatement("else -> %S", "Unknown status")
    .endControlFlow()
    .build()

// Try-catch block
val tryCatchCode = CodeBlock.builder()
    .beginControlFlow("try")
    .addStatement("val result = riskyOperation()")
    .addStatement("return result")
    .nextControlFlow("catch (e: %T)", Exception::class)
    .addStatement("logger.error(%S, e)", "Operation failed")
    .addStatement("throw e")
    .endControlFlow()
    .build()

// Named parameters for complex formatting
val namedCode = CodeBlock.builder()
    .addNamed(
        "val \$field:L = \$type:T(\$value:S)",
        mapOf(
            "field" to "username",
            "type" to String::class,
            "value" to "john_doe"
        )
    )
    .build()

// Joining code blocks
val parameters = listOf("name", "age", "email")
val paramCode = parameters.map { CodeBlock.of("%L", it) }
val joinedParams = CodeBlock.join(paramCode, ", ")

Template Placeholders and Formatting

KotlinPoet supports various placeholder types for different kinds of code substitution.

// Placeholder types for CodeBlock.of() and related methods:
// %S - String literals (automatically escaped)
// %L - Literals (numbers, booleans, raw code)
// %N - Names (identifiers, avoiding keywords)
// %T - Types (TypeName instances, handles imports)
// %M - Members (MemberName instances, handles imports)
// %% - Literal percent sign

Usage Examples:

// String literals (%S) - automatically escaped
val stringLiteral = CodeBlock.of("val message = %S", "Hello \"World\"")
// Result: val message = "Hello \"World\""

// Literals (%L) - raw values
val numberLiteral = CodeBlock.of("val count = %L", 42)
val booleanLiteral = CodeBlock.of("val isActive = %L", true)
val rawCode = CodeBlock.of("val result = %L", "computeValue()")

// Names (%N) - identifiers that avoid keywords
val identifier = CodeBlock.of("val %N = %S", "class", "This would be escaped")
// Result: val `class` = "This would be escaped"

// Types (%T) - TypeName instances with import handling
val typeUsage = CodeBlock.of("val list = %T<%T>()", ArrayList::class, String::class)
// Result: val list = ArrayList<String>()
// Imports: import java.util.ArrayList, import kotlin.String

// Members (%M) - MemberName instances with import handling
val memberUsage = CodeBlock.of(
    "return %M(items)",
    MemberName.get(ClassName.get("kotlin.collections", "Collections"), "sort")
)

// Complex template with multiple placeholders
val complexTemplate = CodeBlock.of(
    "val %N: %T = %T.%M(%S, %L)",
    "result",
    String::class,
    String::class,
    MemberName.get(String::class, "format"),
    "Value: %d",
    42
)

// Template with collections
val listTemplate = CodeBlock.of(
    "val items = %T(%L)",
    LIST,
    listOf(1, 2, 3).joinToString(", ")
)

Member Name Resolution

Manages references to class members (functions, properties, constants) with automatic import resolution.

/**
 * Reference to a member (property, function, constructor, etc.)
 */
class MemberName {
    /** The enclosing type that contains this member */
    val enclosingClassName: ClassName?
    
    /** The package name if this is a top-level member */
    val packageName: String
    
    /** The simple name of the member */
    val simpleName: String
    
    /** Whether this member can be imported */
    val isExtension: Boolean
    
    companion object {
        /** Creates a member reference for a class member */
        fun get(enclosingClassName: ClassName, simpleName: String): MemberName
        
        /** Creates a member reference for a top-level member */
        fun get(packageName: String, simpleName: String): MemberName
        
        /** Creates a member reference from a Java Class and member name */
        fun get(clazz: Class<*>, simpleName: String): MemberName
        
        /** Creates a member reference from a KClass and member name */
        fun get(klass: KClass<*>, simpleName: String): MemberName
    }
}

Usage Examples:

// Class member references
val stringFormat = MemberName.get(String::class, "format")
val listOf = MemberName.get(ClassName.get("kotlin.collections", "Collections"), "listOf")

// Top-level function references
val println = MemberName.get("kotlin.io", "println")
val maxOf = MemberName.get("kotlin", "maxOf")

// Extension function references
val stringIsBlank = MemberName.get(String::class, "isBlank")

// Using member names in code
val memberUsageCode = CodeBlock.builder()
    .addStatement("val greeting = %M(%S, name)", stringFormat, "Hello, %s!")
    .addStatement("%M(greeting)", println)
    .addStatement("val numbers = %M(1, 2, 3)", listOf)
    .build()

// Member names for constants
val piConstant = MemberName.get(Math::class, "PI")
val maxValue = MemberName.get(Integer::class, "MAX_VALUE")

val constantUsage = CodeBlock.of(
    "val area = %M * radius * radius",
    piConstant
)

Advanced Code Generation Patterns

Common patterns and techniques for generating complex code structures.

Builder Pattern Generation:

val builderClass = TypeSpec.classBuilder("PersonBuilder")
    .addProperty(
        PropertySpec.varBuilder("name", String::class.copy(nullable = true))
            .addModifiers(KModifier.PRIVATE)
            .initializer("null")
            .build()
    )
    .addProperty(
        PropertySpec.varBuilder("age", Int::class.copy(nullable = true))
            .addModifiers(KModifier.PRIVATE)
            .initializer("null")
            .build()
    )
    .addFunction(
        FunSpec.builder("name")
            .addParameter("name", String::class)
            .returns(ClassName("", "PersonBuilder"))
            .addStatement("this.name = name")
            .addStatement("return this")
            .build()
    )
    .addFunction(
        FunSpec.builder("age")
            .addParameter("age", Int::class)
            .returns(ClassName("", "PersonBuilder"))
            .addStatement("this.age = age")
            .addStatement("return this")
            .build()
    )
    .addFunction(
        FunSpec.builder("build")
            .returns(ClassName("", "Person"))
            .beginControlFlow("return %T(", ClassName("", "Person"))
            .addStatement("name = checkNotNull(name) { %S }", "Name is required")
            .addStatement("age = checkNotNull(age) { %S }", "Age is required")
            .endControlFlow(")")
            .build()
    )
    .build()

Sealed Class Generation:

val sealedResult = TypeSpec.classBuilder("Result")
    .addModifiers(KModifier.SEALED)
    .addTypeVariable(TypeVariableName.get("T"))
    .addType(
        TypeSpec.classBuilder("Success")
            .addModifiers(KModifier.DATA)
            .addTypeVariable(TypeVariableName.get("T"))
            .superclass(
                ClassName("", "Result").parameterizedBy(TypeVariableName.get("T"))
            )
            .primaryConstructor(
                FunSpec.constructorBuilder()
                    .addParameter("data", TypeVariableName.get("T"))
                    .build()
            )
            .addProperty(
                PropertySpec.builder("data", TypeVariableName.get("T"))
                    .initializer("data")
                    .build()
            )
            .build()
    )
    .addType(
        TypeSpec.classBuilder("Error")
            .addModifiers(KModifier.DATA)
            .addTypeVariable(TypeVariableName.get("T"))
            .superclass(
                ClassName("", "Result").parameterizedBy(TypeVariableName.get("T"))
            )
            .primaryConstructor(
                FunSpec.constructorBuilder()
                    .addParameter("exception", Throwable::class)
                    .build()
            )
            .addProperty(
                PropertySpec.builder("exception", Throwable::class)
                    .initializer("exception")
                    .build()
            )
            .build()
    )
    .build()

DSL Generation:

val dslBuilder = TypeSpec.classBuilder("HtmlBuilder")
    .addFunction(
        FunSpec.builder("tag")
            .addParameter("name", String::class)
            .addParameter(
                "block",
                LambdaTypeName.get(
                    receiver = ClassName("", "TagBuilder"),
                    returnType = UNIT
                )
            )
            .addStatement("val tagBuilder = TagBuilder(name)")
            .addStatement("tagBuilder.block()")
            .addStatement("append(tagBuilder.build())")
            .build()
    )
    .addFunction(
        FunSpec.builder("text")
            .addParameter("content", String::class)
            .addStatement("append(content)")
            .build()
    )
    .build()

Error Handling in Code Generation

Common patterns for handling errors and validation during code generation.

// Validation in generated code
val validatedProperty = PropertySpec.varBuilder("email", String::class)
    .setter(
        FunSpec.setterBuilder()
            .addParameter("value", String::class)
            .beginControlFlow("require(value.contains('@'))")
            .addStatement("%S", "Invalid email format")
            .endControlFlow()
            .addStatement("field = value")
            .build()
    )
    .build()

// Exception handling in generated methods
val safeOperation = FunSpec.builder("safeRead")
    .addParameter("filename", String::class)
    .returns(String::class.copy(nullable = true))
    .beginControlFlow("return try")
    .addStatement("%T(filename).readText()", File::class)
    .nextControlFlow("catch (e: %T)", IOException::class)
    .addStatement("null")
    .endControlFlow()
    .build()

Install with Tessl CLI

npx tessl i tessl/maven-com-squareup--kotlinpoet

docs

annotations-and-metadata.md

code-generation.md

file-and-type-generation.md

function-and-property-generation.md

index.md

type-system.md

utilities.md

tile.json