Skip to content

Commit cfdc213

Browse files
IRusEgorand
andauthored
Add support for Context Parameters (#2094) (#2112)
* Add support for Context Parameters (#2094) * Add support for Context Parameters (#2094) * Add support for Context Parameters (#2094) * Add support for Context Parameters (#2094) Co-authored-by: Egor Andreevich <[email protected]> * Add support for Context Parameters (#2094) * Add support for Context Parameters (#2094) * Add support for Context Parameters (#2094) --------- Co-authored-by: Egor Andreevich <[email protected]>
1 parent 0df47ed commit cfdc213

File tree

15 files changed

+842
-16
lines changed

15 files changed

+842
-16
lines changed

build.gradle.kts

-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import com.diffplug.gradle.spotless.SpotlessExtension
1717
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
1818
import org.jetbrains.dokka.gradle.DokkaExtension
19-
import org.jetbrains.dokka.gradle.DokkaTask
2019
import org.jetbrains.dokka.gradle.formats.DokkaPublication
2120
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2221
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension

docs/changelog.md

+6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ Change Log
33

44
## Unreleased
55

6+
Thanks to [@IRus][IRus] for contributing to this release.
7+
8+
_2025-04-15_
9+
10+
* New: Support for Context Parameters (#2094)
11+
612
## Version 2.1.0
713

814
Thanks to [@ForteScarlet][ForteScarlet], [@TrevorSStone][TrevorSStone],

docs/context-parameters.md

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
Context Parameters
2+
==================
3+
4+
KotlinPoet supports Kotlin's context parameters feature, which allows you to add context parameters to functions and properties. Context parameters are added using the `contextParameter()` method:
5+
6+
```kotlin
7+
val greet = FunSpec.builder("greet")
8+
.contextParameter("user", String::class)
9+
.addStatement("println(\"Hello, ${'$'}user!\")")
10+
.build()
11+
```
12+
13+
The code above generates:
14+
15+
```kotlin
16+
context(user: kotlin.String)
17+
public fun greet() {
18+
println("Hello, $user!")
19+
}
20+
```
21+
22+
You can add multiple context parameters:
23+
24+
```kotlin
25+
val processData = FunSpec.builder("processData")
26+
.contextParameter("logger", ClassName("com.example", "Logger"))
27+
.contextParameter("config", ClassName("com.example", "Config"))
28+
.addStatement("logger.info(\"Processing with config: ${'$'}config\")")
29+
.build()
30+
```
31+
32+
This generates:
33+
34+
```kotlin
35+
context(logger: com.example.Logger, config: com.example.Config)
36+
public fun processData() {
37+
logger.info("Processing with config: $config")
38+
}
39+
```
40+
41+
**Note:** context receivers and context parameters cannot be used together.

kotlinpoet/api/kotlinpoet.api

+28-4
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,22 @@ public final class com/squareup/kotlinpoet/CodeBlocks {
154154
public static final fun withIndent (Lcom/squareup/kotlinpoet/CodeBlock$Builder;Lkotlin/jvm/functions/Function1;)Lcom/squareup/kotlinpoet/CodeBlock$Builder;
155155
}
156156

157+
public final class com/squareup/kotlinpoet/ContextParameter {
158+
public fun <init> (Lcom/squareup/kotlinpoet/TypeName;)V
159+
public fun <init> (Ljava/lang/String;Lcom/squareup/kotlinpoet/TypeName;)V
160+
public fun equals (Ljava/lang/Object;)Z
161+
public final fun getName ()Ljava/lang/String;
162+
public final fun getType ()Lcom/squareup/kotlinpoet/TypeName;
163+
public fun hashCode ()I
164+
public fun toString ()Ljava/lang/String;
165+
}
166+
167+
public abstract interface class com/squareup/kotlinpoet/ContextParameterizable {
168+
}
169+
170+
public abstract interface class com/squareup/kotlinpoet/ContextParameterizable$Builder {
171+
}
172+
157173
public abstract interface class com/squareup/kotlinpoet/ContextReceivable {
158174
}
159175

@@ -293,14 +309,15 @@ public final class com/squareup/kotlinpoet/FileSpec$Companion {
293309
public static synthetic fun scriptBuilder$default (Lcom/squareup/kotlinpoet/FileSpec$Companion;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
294310
}
295311

296-
public final class com/squareup/kotlinpoet/FunSpec : com/squareup/kotlinpoet/Annotatable, com/squareup/kotlinpoet/ContextReceivable, com/squareup/kotlinpoet/Documentable, com/squareup/kotlinpoet/OriginatingElementsHolder, com/squareup/kotlinpoet/Taggable {
312+
public final class com/squareup/kotlinpoet/FunSpec : com/squareup/kotlinpoet/Annotatable, com/squareup/kotlinpoet/ContextParameterizable, com/squareup/kotlinpoet/ContextReceivable, com/squareup/kotlinpoet/Documentable, com/squareup/kotlinpoet/OriginatingElementsHolder, com/squareup/kotlinpoet/Taggable {
297313
public static final field Companion Lcom/squareup/kotlinpoet/FunSpec$Companion;
298314
public static final fun builder (Lcom/squareup/kotlinpoet/MemberName;)Lcom/squareup/kotlinpoet/FunSpec$Builder;
299315
public static final fun builder (Ljava/lang/String;)Lcom/squareup/kotlinpoet/FunSpec$Builder;
300316
public static final fun constructorBuilder ()Lcom/squareup/kotlinpoet/FunSpec$Builder;
301317
public fun equals (Ljava/lang/Object;)Z
302318
public fun getAnnotations ()Ljava/util/List;
303319
public final fun getBody ()Lcom/squareup/kotlinpoet/CodeBlock;
320+
public fun getContextParameters ()Ljava/util/List;
304321
public fun getContextReceiverTypes ()Ljava/util/List;
305322
public final fun getDelegateConstructor ()Ljava/lang/String;
306323
public final fun getDelegateConstructorArguments ()Ljava/util/List;
@@ -330,7 +347,7 @@ public final class com/squareup/kotlinpoet/FunSpec : com/squareup/kotlinpoet/Ann
330347
public fun toString ()Ljava/lang/String;
331348
}
332349

333-
public final class com/squareup/kotlinpoet/FunSpec$Builder : com/squareup/kotlinpoet/Annotatable$Builder, com/squareup/kotlinpoet/ContextReceivable$Builder, com/squareup/kotlinpoet/Documentable$Builder, com/squareup/kotlinpoet/OriginatingElementsHolder$Builder, com/squareup/kotlinpoet/Taggable$Builder {
350+
public final class com/squareup/kotlinpoet/FunSpec$Builder : com/squareup/kotlinpoet/Annotatable$Builder, com/squareup/kotlinpoet/ContextParameterizable$Builder, com/squareup/kotlinpoet/ContextReceivable$Builder, com/squareup/kotlinpoet/Documentable$Builder, com/squareup/kotlinpoet/OriginatingElementsHolder$Builder, com/squareup/kotlinpoet/Taggable$Builder {
334351
public synthetic fun addAnnotation (Lcom/squareup/kotlinpoet/AnnotationSpec;)Lcom/squareup/kotlinpoet/Annotatable$Builder;
335352
public fun addAnnotation (Lcom/squareup/kotlinpoet/AnnotationSpec;)Lcom/squareup/kotlinpoet/FunSpec$Builder;
336353
public synthetic fun addAnnotation (Lcom/squareup/kotlinpoet/ClassName;)Lcom/squareup/kotlinpoet/Annotatable$Builder;
@@ -375,6 +392,9 @@ public final class com/squareup/kotlinpoet/FunSpec$Builder : com/squareup/kotlin
375392
public final fun callThisConstructor ([Ljava/lang/String;)Lcom/squareup/kotlinpoet/FunSpec$Builder;
376393
public static synthetic fun callThisConstructor$default (Lcom/squareup/kotlinpoet/FunSpec$Builder;[Lcom/squareup/kotlinpoet/CodeBlock;ILjava/lang/Object;)Lcom/squareup/kotlinpoet/FunSpec$Builder;
377394
public final fun clearBody ()Lcom/squareup/kotlinpoet/FunSpec$Builder;
395+
public synthetic fun contextParameter (Lcom/squareup/kotlinpoet/TypeName;)Lcom/squareup/kotlinpoet/ContextParameterizable$Builder;
396+
public synthetic fun contextParameter (Ljava/lang/String;Lcom/squareup/kotlinpoet/TypeName;)Lcom/squareup/kotlinpoet/ContextParameterizable$Builder;
397+
public synthetic fun contextParameters (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/ContextParameterizable$Builder;
378398
public synthetic fun contextReceivers (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/ContextReceivable$Builder;
379399
public final fun endControlFlow ()Lcom/squareup/kotlinpoet/FunSpec$Builder;
380400
public fun getAnnotations ()Ljava/util/List;
@@ -720,7 +740,7 @@ public final class com/squareup/kotlinpoet/ParameterizedTypeNames {
720740
public static final fun get (Ljava/lang/reflect/ParameterizedType;)Lcom/squareup/kotlinpoet/ParameterizedTypeName;
721741
}
722742

723-
public final class com/squareup/kotlinpoet/PropertySpec : com/squareup/kotlinpoet/Annotatable, com/squareup/kotlinpoet/ContextReceivable, com/squareup/kotlinpoet/Documentable, com/squareup/kotlinpoet/OriginatingElementsHolder, com/squareup/kotlinpoet/Taggable {
743+
public final class com/squareup/kotlinpoet/PropertySpec : com/squareup/kotlinpoet/Annotatable, com/squareup/kotlinpoet/ContextParameterizable, com/squareup/kotlinpoet/ContextReceivable, com/squareup/kotlinpoet/Documentable, com/squareup/kotlinpoet/OriginatingElementsHolder, com/squareup/kotlinpoet/Taggable {
724744
public static final field Companion Lcom/squareup/kotlinpoet/PropertySpec$Companion;
725745
public static final fun builder (Ljava/lang/String;Lcom/squareup/kotlinpoet/TypeName;Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
726746
public static final fun builder (Ljava/lang/String;Lcom/squareup/kotlinpoet/TypeName;[Lcom/squareup/kotlinpoet/KModifier;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
@@ -730,6 +750,7 @@ public final class com/squareup/kotlinpoet/PropertySpec : com/squareup/kotlinpoe
730750
public static final fun builder (Ljava/lang/String;Lkotlin/reflect/KClass;[Lcom/squareup/kotlinpoet/KModifier;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
731751
public fun equals (Ljava/lang/Object;)Z
732752
public fun getAnnotations ()Ljava/util/List;
753+
public fun getContextParameters ()Ljava/util/List;
733754
public fun getContextReceiverTypes ()Ljava/util/List;
734755
public final fun getDelegated ()Z
735756
public final fun getGetter ()Lcom/squareup/kotlinpoet/FunSpec;
@@ -754,7 +775,7 @@ public final class com/squareup/kotlinpoet/PropertySpec : com/squareup/kotlinpoe
754775
public fun toString ()Ljava/lang/String;
755776
}
756777

757-
public final class com/squareup/kotlinpoet/PropertySpec$Builder : com/squareup/kotlinpoet/Annotatable$Builder, com/squareup/kotlinpoet/ContextReceivable$Builder, com/squareup/kotlinpoet/Documentable$Builder, com/squareup/kotlinpoet/OriginatingElementsHolder$Builder, com/squareup/kotlinpoet/Taggable$Builder {
778+
public final class com/squareup/kotlinpoet/PropertySpec$Builder : com/squareup/kotlinpoet/Annotatable$Builder, com/squareup/kotlinpoet/ContextParameterizable$Builder, com/squareup/kotlinpoet/ContextReceivable$Builder, com/squareup/kotlinpoet/Documentable$Builder, com/squareup/kotlinpoet/OriginatingElementsHolder$Builder, com/squareup/kotlinpoet/Taggable$Builder {
758779
public synthetic fun addAnnotation (Lcom/squareup/kotlinpoet/AnnotationSpec;)Lcom/squareup/kotlinpoet/Annotatable$Builder;
759780
public fun addAnnotation (Lcom/squareup/kotlinpoet/AnnotationSpec;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
760781
public synthetic fun addAnnotation (Lcom/squareup/kotlinpoet/ClassName;)Lcom/squareup/kotlinpoet/Annotatable$Builder;
@@ -774,6 +795,9 @@ public final class com/squareup/kotlinpoet/PropertySpec$Builder : com/squareup/k
774795
public final fun addTypeVariable (Lcom/squareup/kotlinpoet/TypeVariableName;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
775796
public final fun addTypeVariables (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
776797
public final fun build ()Lcom/squareup/kotlinpoet/PropertySpec;
798+
public synthetic fun contextParameter (Lcom/squareup/kotlinpoet/TypeName;)Lcom/squareup/kotlinpoet/ContextParameterizable$Builder;
799+
public synthetic fun contextParameter (Ljava/lang/String;Lcom/squareup/kotlinpoet/TypeName;)Lcom/squareup/kotlinpoet/ContextParameterizable$Builder;
800+
public synthetic fun contextParameters (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/ContextParameterizable$Builder;
777801
public final fun delegate (Lcom/squareup/kotlinpoet/CodeBlock;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
778802
public final fun delegate (Ljava/lang/String;[Ljava/lang/Object;)Lcom/squareup/kotlinpoet/PropertySpec$Builder;
779803
public fun getAnnotations ()Ljava/util/List;

kotlinpoet/src/jvmMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt

+33
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,39 @@ internal class CodeWriter(
185185
}
186186
}
187187

188+
/**
189+
* Emits the `context` block for [contextParameters].
190+
*/
191+
fun emitContextParameters(contextParameters: List<ContextParameter>, suffix: String = "") {
192+
emitContextParameters(
193+
contextParameters.map { CodeBlock.of("%L: %T", it.name, it.type) },
194+
suffix,
195+
)
196+
}
197+
198+
/**
199+
* Emits the `context` block for [contextParameters].
200+
*/
201+
@JvmName("emitContextParametersFromTypeNames")
202+
fun emitContextParameters(contextParameters: List<TypeName>, suffix: String = "") {
203+
emitContextParameters(
204+
contextParameters.map { CodeBlock.of("%T", it) },
205+
suffix,
206+
)
207+
}
208+
209+
@JvmName("emitContextParametersFromCodeBlocks")
210+
private fun emitContextParameters(
211+
contextParameters: List<CodeBlock>,
212+
suffix: String = "",
213+
) {
214+
if (contextParameters.isNotEmpty()) {
215+
val parameters = contextParameters.joinToCode(prefix = "context(", suffix = ")")
216+
emitCode(parameters)
217+
emit(suffix)
218+
}
219+
}
220+
188221
/**
189222
* Emit type variables with their bounds. If a type variable has more than a single bound - call
190223
* [emitWhereBlock] with same input to produce an additional `where` block.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Copyright (C) 2025 Square, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.squareup.kotlinpoet
17+
18+
import java.lang.reflect.Type
19+
import kotlin.reflect.KClass
20+
21+
/**
22+
* Represents a context parameter with a name and type.
23+
*/
24+
public class ContextParameter(
25+
public val name: String,
26+
public val type: TypeName,
27+
) {
28+
public constructor(type: TypeName) : this(name = "_", type)
29+
30+
override fun equals(other: Any?): Boolean {
31+
if (this === other) return true
32+
if (javaClass != other?.javaClass) return false
33+
34+
other as ContextParameter
35+
36+
if (name != other.name) return false
37+
if (type != other.type) return false
38+
39+
return true
40+
}
41+
42+
override fun hashCode(): Int {
43+
var result = name.hashCode()
44+
result = 31 * result + type.hashCode()
45+
return result
46+
}
47+
48+
override fun toString(): String = "$name: $type"
49+
}
50+
51+
/**
52+
* A KotlinPoet spec type that can have context parameters.
53+
*/
54+
public interface ContextParameterizable {
55+
/**
56+
* The context parameters of this type.
57+
*/
58+
@ExperimentalKotlinPoetApi
59+
public val contextParameters: List<ContextParameter>
60+
61+
/**
62+
* The builder analogue to [ContextParameterizable] types.
63+
*/
64+
public interface Builder<out T : Builder<T>> {
65+
/**
66+
* Mutable list of the current context parameters this builder contains.
67+
*/
68+
@ExperimentalKotlinPoetApi
69+
public val contextParameters: MutableList<ContextParameter>
70+
71+
/**
72+
* Adds the given [parameters] to this type's list of context parameters.
73+
*/
74+
@Suppress("UNCHECKED_CAST")
75+
@ExperimentalKotlinPoetApi
76+
public fun contextParameters(parameters: Iterable<ContextParameter>): T = apply {
77+
contextParameters += parameters
78+
} as T
79+
80+
/**
81+
* Adds a context parameter with the given [name] and [type] to this type's list of context parameters.
82+
*/
83+
@ExperimentalKotlinPoetApi
84+
public fun contextParameter(name: String, type: TypeName): T =
85+
contextParameters(listOf(ContextParameter(name, type)))
86+
87+
/**
88+
* Adds a context parameter with the name "_" and [type] to this type's list of context parameters.
89+
*/
90+
@ExperimentalKotlinPoetApi
91+
public fun contextParameter(type: TypeName): T =
92+
contextParameters(listOf(ContextParameter(type)))
93+
94+
/**
95+
* Adds a context parameter with the given [name] and [type] to this type's list of context parameters.
96+
*/
97+
@DelicateKotlinPoetApi(
98+
message = "Java reflection APIs don't give complete information on Kotlin types. Consider " +
99+
"using the kotlinpoet-metadata APIs instead.",
100+
)
101+
@ExperimentalKotlinPoetApi
102+
public fun contextParameter(name: String, type: Type): T =
103+
contextParameter(name, type.asTypeName())
104+
105+
/**
106+
* Adds a context parameter with the given [name] and [type] to this type's list of context parameters.
107+
*/
108+
@ExperimentalKotlinPoetApi
109+
public fun contextParameter(name: String, type: KClass<*>): T =
110+
contextParameter(name, type.asTypeName())
111+
}
112+
}
113+
114+
@ExperimentalKotlinPoetApi
115+
internal fun ContextParameterizable.Builder<*>.buildContextParameters() =
116+
ContextParameters(contextParameters.toImmutableList())
117+
118+
@JvmInline
119+
@ExperimentalKotlinPoetApi
120+
internal value class ContextParameters(
121+
override val contextParameters: List<ContextParameter>,
122+
) : ContextParameterizable

0 commit comments

Comments
 (0)