Upgrade kotlinpoet to 1.16.0 am: af7bd99cda
Original change: https://android-review.googlesource.com/c/platform/external/kotlinpoet/+/2967683
Change-Id: I5922082e30e41d299676b15095b223a761ca1b40
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 0bd44d1..187d0ac 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -13,24 +13,33 @@
jobs:
jvm:
- runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ os:
+ - macos-latest
+ - ubuntu-latest
+ - windows-latest
+
+ runs-on: ${{ matrix.os }}
steps:
- name: Checkout
uses: actions/checkout@v4
- - name: Validate Gradle Wrapper
- uses: gradle/wrapper-validation-action@v1
-
- name: Configure JDK
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 19
- - name: Test
+ - name: Full build
+ if: matrix.os == 'ubuntu-latest'
run: ./gradlew build
+ - name: KotlinPoet check
+ if: "matrix.os != 'ubuntu-latest'"
+ run: ./gradlew :kotlinpoet:check
+
build-docs:
runs-on: ubuntu-latest
if: github.repository == 'square/kotlinpoet'
@@ -40,7 +49,7 @@
uses: actions/checkout@v4
- name: Configure JDK
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 19
@@ -49,7 +58,7 @@
run: ./gradlew dokkaHtml
- name: Set up Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: 3.8
diff --git a/.github/workflows/gradle-wrapper.yaml b/.github/workflows/gradle-wrapper.yaml
new file mode 100644
index 0000000..d82e291
--- /dev/null
+++ b/.github/workflows/gradle-wrapper.yaml
@@ -0,0 +1,15 @@
+name: gradle-wrapper
+
+on:
+ pull_request:
+ paths:
+ - 'gradlew'
+ - 'gradlew.bat'
+ - 'gradle/wrapper/**'
+
+jobs:
+ validate:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: gradle/wrapper-validation-action@v1
diff --git a/.github/workflows/mkdocs-requirements.txt b/.github/workflows/mkdocs-requirements.txt
index 213346e..5f787ff 100644
--- a/.github/workflows/mkdocs-requirements.txt
+++ b/.github/workflows/mkdocs-requirements.txt
@@ -1,18 +1,18 @@
click==8.1.7
future==0.18.3
-Jinja2==3.1.2
+Jinja2==3.1.3
livereload==2.6.3
lunr==0.7.0.post1
MarkupSafe==2.1.3
mkdocs==1.5.3
-mkdocs-macros-plugin==1.0.4
-mkdocs-material==9.4.5
-mkdocs-material-extensions==1.2
-Pygments==2.16.1
-pymdown-extensions==10.3
+mkdocs-macros-plugin==1.0.5
+mkdocs-material==9.5.4
+mkdocs-material-extensions==1.3.1
+Pygments==2.17.2
+pymdown-extensions==10.7
python-dateutil==2.8.2
PyYAML==6.0.1
repackage==0.7.3
six==1.16.0
-termcolor==2.3.0
-tornado==6.3.3
+termcolor==2.4.0
+tornado==6.4
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index cb8950a..1a2f3f9 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -20,7 +20,7 @@
uses: actions/checkout@v4
- name: Configure JDK
- uses: actions/setup-java@v3
+ uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 19
@@ -36,7 +36,7 @@
run: ./gradlew dokkaHtml
- name: Set up Python
- uses: actions/setup-python@v4
+ uses: actions/setup-python@v5
with:
python-version: 3.8
diff --git a/.gitignore b/.gitignore
index 75e7808..9310aea 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,6 +18,7 @@
# Mkdocs files
docs/1.x/*
+site
obj
diff --git a/Android.bp b/Android.bp
index 87c062c..1505c4a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -15,12 +15,14 @@
java_library_host {
name: "kotlinpoet",
- srcs: ["kotlinpoet/src/main/**/*.kt"],
+ srcs: [
+ "kotlinpoet/src/commonMain/**/*.kt",
+ ],
kotlincflags: ["-Xjvm-default=all"],
static_libs: [
"kotlin-stdlib-jdk8",
"kotlin-reflect",
- ]
+ ],
}
java_library_host {
@@ -30,6 +32,6 @@
static_libs: [
"javapoet",
"kotlin-stdlib-jdk8",
- "kotlinpoet"
- ]
+ "kotlinpoet",
+ ],
}
diff --git a/METADATA b/METADATA
index fbd5831..127fc45 100644
--- a/METADATA
+++ b/METADATA
@@ -1,23 +1,20 @@
# This project was upgraded with external_updater.
-# Usage: tools/external_updater/updater.sh update kotlinpoet
+# Usage: tools/external_updater/updater.sh update external/kotlinpoet
# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
name: "kotlinpoet"
description: "A Kotlin API for generating .kt source files"
third_party {
- url {
- type: HOMEPAGE
- value: "https://square.github.io/kotlinpoet/"
- }
- url {
- type: GIT
- value: "https://github.com/square/kotlinpoet.git"
- }
- version: "562b5c4a5297e85ae2727fc9f6c4e96e770d7420"
license_type: NOTICE
last_upgrade_date {
- year: 2023
- month: 10
- day: 10
+ year: 2024
+ month: 2
+ day: 15
+ }
+ homepage: "https://square.github.io/kotlinpoet/"
+ identifier {
+ type: "Git"
+ value: "https://github.com/square/kotlinpoet.git"
+ version: "1.16.0"
}
}
diff --git a/RELEASING.md b/RELEASING.md
index 418d952..7c064c7 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -10,4 +10,6 @@
7. `git push && git push --tags`.
This will trigger a GitHub Action workflow which will create a GitHub release and upload the
-release artifacts to Maven Central.
+release artifacts to [Maven Central][maven-central].
+
+ [maven-central]: https://repo.maven.apache.org/maven2/com/squareup/kotlinpoet/
diff --git a/build.gradle.kts b/build.gradle.kts
index 3522375..16fa872 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -17,10 +17,12 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.jetbrains.dokka.gradle.DokkaTask
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
+import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
+ alias(libs.plugins.kotlin.multiplatform) apply false
alias(libs.plugins.kotlin.jvm) apply false
alias(libs.plugins.ksp) apply false
alias(libs.plugins.dokka) apply false
@@ -53,12 +55,18 @@
options.release.set(8)
}
- apply(plugin = "org.jetbrains.kotlin.jvm")
if ("test" !in name && buildFile.exists()) {
apply(plugin = "org.jetbrains.dokka")
apply(plugin = "com.vanniktech.maven.publish")
- configure<KotlinProjectExtension> {
- explicitApi()
+ pluginManager.withPlugin("org.jetbrains.kotlin.multiplatform") {
+ configure<KotlinMultiplatformExtension> {
+ explicitApi()
+ }
+ }
+ pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
+ configure<KotlinProjectExtension> {
+ explicitApi()
+ }
}
afterEvaluate {
tasks.named<DokkaTask>("dokkaHtml") {
@@ -70,7 +78,6 @@
}
}
}
-
apply(plugin = "com.diffplug.spotless")
configure<SpotlessExtension> {
kotlin {
@@ -98,7 +105,7 @@
| * See the License for the specific language governing permissions and
| * limitations under the License.
| */
- """.trimMargin()
+ """.trimMargin(),
)
}
}
@@ -106,32 +113,44 @@
// Only enable the extra toolchain tests on CI. Otherwise local development is broken on Apple Silicon macs
// because there are no matching toolchains for several older JDK versions.
if ("CI" in System.getenv()) {
- // Copied from https://github.com/square/retrofit/blob/master/retrofit/build.gradle#L28.
- // Create a test task for each supported JDK. We check every "LTS" + current version.
- val versionsToTest = listOf(8, 11, 17, 19)
- for (majorVersion in versionsToTest) {
- val jdkTest = tasks.register<Test>("testJdk$majorVersion") {
- val javaToolchains = project.extensions.getByType(JavaToolchainService::class)
- javaLauncher.set(javaToolchains.launcherFor {
- languageVersion.set(JavaLanguageVersion.of(majorVersion))
- vendor.set(JvmVendorSpec.AZUL)
- })
+ fun Project.setupCheckTask(testTaskName: String) {
+ // Copied from https://github.com/square/retrofit/blob/master/retrofit/build.gradle#L28.
+ // Create a test task for each supported JDK. We check every "LTS" + current version.
+ val versionsToTest = listOf(8, 11, 17, 19)
+ for (majorVersion in versionsToTest) {
+ val jdkTest = tasks.register<Test>("testJdk$majorVersion") {
+ val javaToolchains = project.extensions.getByType(JavaToolchainService::class)
+ javaLauncher.set(
+ javaToolchains.launcherFor {
+ languageVersion.set(JavaLanguageVersion.of(majorVersion))
+ vendor.set(JvmVendorSpec.AZUL)
+ },
+ )
- description = "Runs the test suite on JDK $majorVersion"
- group = LifecycleBasePlugin.VERIFICATION_GROUP
+ description = "Runs the test suite on JDK $majorVersion"
+ group = LifecycleBasePlugin.VERIFICATION_GROUP
- // Copy inputs from normal Test task.
- val testTask = tasks.getByName<Test>("test")
- classpath = testTask.classpath
- testClassesDirs = testTask.testClassesDirs
+ // Copy inputs from normal Test task.
+ val testTask =
+ tasks.getByName<Test>(testTaskName)
- testLogging {
- exceptionFormat = TestExceptionFormat.FULL
+ classpath = testTask.classpath
+ testClassesDirs = testTask.testClassesDirs
+
+ testLogging {
+ exceptionFormat = TestExceptionFormat.FULL
+ }
+ }
+ tasks.named("check").configure {
+ dependsOn(jdkTest)
}
}
- tasks.named("check").configure {
- dependsOn(jdkTest)
- }
+ }
+ pluginManager.withPlugin("org.jetbrains.kotlin.multiplatform") {
+ setupCheckTask("jvmTest")
+ }
+ pluginManager.withPlugin("org.jetbrains.kotlin.jvm") {
+ setupCheckTask("test")
}
}
}
@@ -140,6 +159,6 @@
nonPublicMarkers += "com.squareup.kotlinpoet.ExperimentalKotlinPoetApi"
ignoredProjects += listOf(
"interop", // Empty middle package
- "test-processor" // Test only
+ "test-processor", // Test only
)
}
diff --git a/docs/annotations.md b/docs/annotations.md
new file mode 100644
index 0000000..47a2cd9
--- /dev/null
+++ b/docs/annotations.md
@@ -0,0 +1,118 @@
+Annotations
+===========
+
+Simple annotations are easy:
+
+```kotlin
+val test = FunSpec.builder("test string equality")
+ .addAnnotation(Test::class)
+ .addStatement("assertThat(%1S).isEqualTo(%1S)", "foo")
+ .build()
+```
+
+Which generates this function with an `@Test` annotation:
+
+```kotlin
+@Test
+fun `test string equality`() {
+ assertThat("foo").isEqualTo("foo")
+}
+```
+
+Use `AnnotationSpec.builder()` to set properties on annotations:
+
+```kotlin
+val logRecord = FunSpec.builder("recordEvent")
+ .addModifiers(KModifier.ABSTRACT)
+ .addAnnotation(
+ AnnotationSpec.builder(Headers::class)
+ .addMember("accept = %S", "application/json; charset=utf-8")
+ .addMember("userAgent = %S", "Square Cash")
+ .build()
+ )
+ .addParameter("logRecord", LogRecord::class)
+ .returns(LogReceipt::class)
+ .build()
+```
+
+Which generates this annotation with `accept` and `userAgent` properties:
+
+```kotlin
+@Headers(
+ accept = "application/json; charset=utf-8",
+ userAgent = "Square Cash"
+)
+abstract fun recordEvent(logRecord: LogRecord): LogReceipt
+```
+
+When you get fancy, annotation values can be annotations themselves. Use `%L` for embedded
+annotations:
+
+```kotlin
+val headerList = ClassName("", "HeaderList")
+val header = ClassName("", "Header")
+val logRecord = FunSpec.builder("recordEvent")
+ .addModifiers(KModifier.ABSTRACT)
+ .addAnnotation(
+ AnnotationSpec.builder(headerList)
+ .addMember(
+ "[\n⇥%L,\n%L⇤\n]",
+ AnnotationSpec.builder(header)
+ .addMember("name = %S", "Accept")
+ .addMember("value = %S", "application/json; charset=utf-8")
+ .build(),
+ AnnotationSpec.builder(header)
+ .addMember("name = %S", "User-Agent")
+ .addMember("value = %S", "Square Cash")
+ .build()
+ )
+ .build()
+ )
+ .addParameter("logRecord", logRecordName)
+ .returns(logReceipt)
+ .build()
+```
+
+Which generates this:
+
+```kotlin
+@HeaderList(
+ [
+ Header(name = "Accept", value = "application/json; charset=utf-8"),
+ Header(name = "User-Agent", value = "Square Cash")
+ ]
+)
+abstract fun recordEvent(logRecord: LogRecord): LogReceipt
+```
+
+KotlinPoet supports use-site targets for annotations:
+
+```kotlin
+val utils = FileSpec.builder("com.example", "Utils")
+ .addAnnotation(
+ AnnotationSpec.builder(JvmName::class)
+ .useSiteTarget(UseSiteTarget.FILE)
+ .build()
+ )
+ .addFunction(
+ FunSpec.builder("abs")
+ .receiver(Int::class)
+ .returns(Int::class)
+ .addStatement("return if (this < 0) -this else this")
+ .build()
+ )
+ .build()
+```
+
+Will output this:
+
+```kotlin
+@file:JvmName
+
+package com.example
+
+import kotlin.Int
+import kotlin.jvm.JvmName
+
+fun Int.abs(): Int = if (this < 0) -this else this
+```
diff --git a/docs/anonymous-inner-classes.md b/docs/anonymous-inner-classes.md
new file mode 100644
index 0000000..a2f35da
--- /dev/null
+++ b/docs/anonymous-inner-classes.md
@@ -0,0 +1,44 @@
+Anonymous Inner Classes
+=======================
+
+In the enum code, we used `TypeSpec.anonymousClassBuilder()`. Anonymous inner classes can also be
+used in code blocks. They are values that can be referenced with `%L`:
+
+```kotlin
+val comparator = TypeSpec.anonymousClassBuilder()
+ .addSuperinterface(Comparator::class.parameterizedBy(String::class))
+ .addFunction(
+ FunSpec.builder("compare")
+ .addModifiers(KModifier.OVERRIDE)
+ .addParameter("a", String::class)
+ .addParameter("b", String::class)
+ .returns(Int::class)
+ .addStatement("return %N.length - %N.length", "a", "b")
+ .build()
+ )
+ .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+ .addFunction(
+ FunSpec.builder("sortByLength")
+ .addParameter("strings", List::class.parameterizedBy(String::class))
+ .addStatement("%N.sortedWith(%L)", "strings", comparator)
+ .build()
+ )
+ .build()
+```
+
+This generates a method that contains a class that contains a method:
+
+```kotlin
+class HelloWorld {
+ fun sortByLength(strings: List<String>) {
+ strings.sortedWith(object : Comparator<String> {
+ override fun compare(a: String, b: String): Int = a.length - b.length
+ })
+ }
+}
+```
+
+One particularly tricky part of defining anonymous inner classes is the arguments to the superclass
+constructor. To pass them use `TypeSpec.Builder`'s `addSuperclassConstructorParameter()` method.
diff --git a/docs/callable-references.md b/docs/callable-references.md
new file mode 100644
index 0000000..a7bbdc8
--- /dev/null
+++ b/docs/callable-references.md
@@ -0,0 +1,45 @@
+Callable References
+===================
+
+[Callable references][callable-references] to constructors, functions, and properties may be emitted
+via:
+
+- `ClassName.constructorReference()` for constructors
+- `MemberName.reference()` for functions and properties
+
+For example,
+
+```kotlin
+val helloClass = ClassName("com.example.hello", "Hello")
+val worldFunction: MemberName = helloClass.member("world")
+val byeProperty: MemberName = helloClass.nestedClass("World").member("bye")
+
+val factoriesFun = FunSpec.builder("factories")
+ .addStatement("val hello = %L", helloClass.constructorReference())
+ .addStatement("val world = %L", worldFunction.reference())
+ .addStatement("val bye = %L", byeProperty.reference())
+ .build()
+
+FileSpec.builder("com.example", "HelloWorld")
+ .addFunction(factoriesFun)
+ .build()
+```
+
+would generate:
+
+```kotlin
+package com.example
+
+import com.example.hello.Hello
+
+fun factories() {
+ val hello = ::Hello
+ val world = Hello::world
+ val bye = Hello.World::bye
+}
+```
+
+Top-level classes and members with conflicting names may require aliased imports, as with
+[member names](m-for-members.md).
+
+ [callable-references]: https://kotlinlang.org/docs/reference/reflection.html#callable-references
diff --git a/docs/changelog.md b/docs/changelog.md
index 108d8e0..8f971fe 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -3,6 +3,71 @@
## Unreleased
+## Version 1.16.0
+
+Thanks to [@drawers][drawers], [@rickclephas][rickclephas] for contributing to this release.
+
+_2024-01-18_
+
+ * New: Kotlin 1.9.22.
+ * New: KSP 1.9.22-1.0.16.
+ * New: Add `NameAllocator` API to control keyword pre-allocation (#1803).
+ * Fix: Fix issue with missing `suspend` modifier in `KSTypeReference.toTypeName` (#1793).
+ * Fix: Honour same-package import aliases (#1794).
+ * Fix: Always include parameter docs in the type header (#1800).
+
+## Version 1.15.3
+
+Thanks to [@gabrielittner][gabrielittner] for contributing to this release.
+
+_2023-12-04_
+
+ * Fix: Fix nullability of lambdas in `KSTypeReference.toTypeName` (#1756).
+
+## Version 1.15.2
+
+Thanks to [@evant][evant] for contributing to this release.
+
+_2023-11-30_
+
+ * New: Kotlin 1.9.21.
+ * New: KSP 1.9.21-1.0.15.
+ * New: KSP: more accurately represent function types (#1742).
+
+## Version 1.15.1
+
+_2023-11-19_
+
+ * Fix: Fix a regression introduced by #1637, where a superfluous newline is added to a type's KDoc
+ if it has a primary constructor with no docs (#1727).
+
+## Version 1.15.0
+
+_2023-11-18_
+
+Thanks to [@drawers][drawers], [@fejesjoco][fejesjoco], [@takahirom][takahirom],
+[@martinbonnin][martinbonnin], [@mcarleio][mcarleio] for contributing to this release.
+
+In this release the `:kotlinpoet` module has been converted to a Kotlin Multiplatform module
+(#1654), though for now it only supports the JVM target. **Important**: unless you're building
+with Gradle, you will now need to depend on the `kotlinpoet-jvm` artifact, instead of `kotlinpoet` -
+see [Downloads](index.md#download) for instructions.
+
+ * New: Kotlin 1.9.20.
+ * New: KSP 1.9.20-1.0.14.
+ * New: Extract `TypeSpecHolder` interface for constructs that can hold a TypeSpec and their builders (#1723).
+ * New: Expose relative path from `FileSpec` (#1720).
+ * New: Return the generated path from `FileSpec.writeTo()`. (#1514).
+ * New: Remove default compatibility from unstable types (#1662).
+ * New: Deprecate `TypeSpec.expectClassBuilder()` and `TypeSpec.valueClassBuilder()` (#1589).
+ * New: Add option to convert `KSAnnotation` to `AnnotationSpec` while omitting default values (#1538).
+ * New: Add `FileSpec.builder` convenience for `MemberName` (#1585).
+ * Fix: Set `DecimalFormatSymbols.minusSign` for consistency across locales (#1658).
+ * Fix: Fix link to incremental KSP in KDoc (#1638).
+ * Fix: Emit primary constructor KDoc (#1637).
+ * Change: kotlinx-metadata 0.7.0. This is a breaking change for users of the `:kotlinpoet-metadata`
+ module, as most `Flags`-API extensions have been removed in favor of the now-available first-party versions.
+
## Version 1.14.2
_2023-05-30_
@@ -716,3 +781,7 @@
[Squiry]: https://github.com/Squiry
[Omico]: https://github.com/Omico
[RBusarow]: https://github.com/RBusarow
+ [fejesjoco]: https://github.com/fejesjoco
+ [takahirom]: https://github.com/takahirom
+ [mcarleio]: https://github.com/mcarleio
+ [gabrielittner]: https://github.com/gabrielittner
diff --git a/docs/code-block-format-strings.md b/docs/code-block-format-strings.md
new file mode 100644
index 0000000..86547b1
--- /dev/null
+++ b/docs/code-block-format-strings.md
@@ -0,0 +1,36 @@
+Code Block Format Strings
+=========================
+
+Code blocks may specify the values for their placeholders in a few ways. Only one style may be used
+for each operation on a code block.
+
+## Relative Arguments
+
+Pass an argument value for each placeholder in the format string to `CodeBlock.add()`. In each
+example, we generate code to say "I ate 3 tacos"
+
+```kotlin
+CodeBlock.builder().add("I ate %L %L", 3, "tacos")
+```
+
+## Positional Arguments
+
+Place an integer index (1-based) before the placeholder in the format string to specify which
+argument to use.
+
+```kotlin
+CodeBlock.builder().add("I ate %2L %1L", "tacos", 3)
+```
+
+## Named Arguments
+
+Use the syntax `%argumentName:X` where `X` is the format character and call `CodeBlock.addNamed()`
+with a map containing all argument keys in the format string. Argument names use characters in
+`a-z`, `A-Z`, `0-9`, and `_`, and must start with a lowercase character.
+
+```kotlin
+val map = LinkedHashMap<String, Any>()
+map += "food" to "tacos"
+map += "count" to 3
+CodeBlock.builder().addNamed("I ate %count:L %food:L", map)
+```
diff --git a/docs/code-control-flow.md b/docs/code-control-flow.md
new file mode 100644
index 0000000..f2a12f6
--- /dev/null
+++ b/docs/code-control-flow.md
@@ -0,0 +1,76 @@
+Code & Control Flow
+===================
+
+Most of KotlinPoet's API uses immutable Kotlin objects. There's also builders, method chaining
+and varargs to make the API friendly. KotlinPoet offers models for Kotlin files (`FileSpec`),
+classes, interfaces & objects (`TypeSpec`), type aliases (`TypeAliasSpec`),
+properties (`PropertySpec`), functions & constructors (`FunSpec`), parameters (`ParameterSpec`) and
+annotations (`AnnotationSpec`).
+
+But the _body_ of methods and constructors is not modeled. There's no expression class, no
+statement class or syntax tree nodes. Instead, KotlinPoet uses strings for code blocks, and you can
+take advantage of Kotlin's multiline strings to make this look nice:
+
+```kotlin
+val main = FunSpec.builder("main")
+ .addCode("""
+ |var total = 0
+ |for (i in 0..<10) {
+ | total += i
+ |}
+ |""".trimMargin())
+ .build()
+```
+
+Which generates this:
+
+```kotlin
+fun main() {
+ var total = 0
+ for (i in 0..<10) {
+ total += i
+ }
+}
+```
+
+There are additional APIs to assist with newlines, braces and indentation:
+
+```kotlin
+val main = FunSpec.builder("main")
+ .addStatement("var total = 0")
+ .beginControlFlow("for (i in 0..<10)")
+ .addStatement("total += i")
+ .endControlFlow()
+ .build()
+```
+
+This example is lame because the generated code is constant! Suppose instead of just adding 0 to 10,
+we want to make the operation and range configurable. Here's a method that generates a method:
+
+```kotlin
+private fun computeRange(name: String, from: Int, to: Int, op: String): FunSpec {
+ return FunSpec.builder(name)
+ .returns(Int::class)
+ .addStatement("var result = 1")
+ .beginControlFlow("for (i in $from..<$to)")
+ .addStatement("result = result $op i")
+ .endControlFlow()
+ .addStatement("return result")
+ .build()
+}
+```
+
+And here's what we get when we call `computeRange("multiply10to20", 10, 20, "*")`:
+
+```kotlin
+fun multiply10to20(): kotlin.Int {
+ var result = 1
+ for (i in 10..<20) {
+ result = result * i
+ }
+ return result
+}
+```
+
+Methods generating methods! And since KotlinPoet generates source instead of bytecode, you can
+read through it to make sure it's right.
diff --git a/docs/constructors.md b/docs/constructors.md
new file mode 100644
index 0000000..65eef43
--- /dev/null
+++ b/docs/constructors.md
@@ -0,0 +1,80 @@
+Constructors
+============
+
+`FunSpec` is a slight misnomer; it can also be used for constructors:
+
+```kotlin
+val flux = FunSpec.constructorBuilder()
+ .addParameter("greeting", String::class)
+ .addStatement("this.%N = %N", "greeting", "greeting")
+ .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+ .addProperty("greeting", String::class, KModifier.PRIVATE)
+ .addFunction(flux)
+ .build()
+```
+
+Which generates this:
+
+```kotlin
+class HelloWorld {
+ private val greeting: String
+
+ constructor(greeting: String) {
+ this.greeting = greeting
+ }
+}
+```
+
+For the most part, constructors work just like methods. When emitting code, KotlinPoet will place
+constructors before methods in the output file.
+
+Often times you'll need to generate the primary constructor for a class:
+
+```kotlin
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+ .primaryConstructor(flux)
+ .addProperty("greeting", String::class, KModifier.PRIVATE)
+ .build()
+```
+
+This code, however, generates the following:
+
+```kotlin
+class HelloWorld(greeting: String) {
+ private val greeting: String
+
+ init {
+ this.greeting = greeting
+ }
+}
+```
+
+By default, KotlinPoet won't merge primary constructor parameters and properties, even if they share
+the same name. To achieve the effect, you have to tell KotlinPoet that the property is initialized
+via the constructor parameter:
+
+```kotlin
+val flux = FunSpec.constructorBuilder()
+ .addParameter("greeting", String::class)
+ .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+ .primaryConstructor(flux)
+ .addProperty(
+ PropertySpec.builder("greeting", String::class)
+ .initializer("greeting")
+ .addModifiers(KModifier.PRIVATE)
+ .build()
+ )
+ .build()
+```
+
+Now we're getting the following output:
+
+```kotlin
+class HelloWorld(private val greeting: String)
+```
+
+Notice that KotlinPoet omits `{}` for classes with empty bodies.
diff --git a/docs/enums.md b/docs/enums.md
new file mode 100644
index 0000000..229f98c
--- /dev/null
+++ b/docs/enums.md
@@ -0,0 +1,78 @@
+Enums
+=====
+
+Use `enumBuilder` to create the enum type, and `addEnumConstant()` for each value:
+
+```kotlin
+val helloWorld = TypeSpec.enumBuilder("Roshambo")
+ .addEnumConstant("ROCK")
+ .addEnumConstant("SCISSORS")
+ .addEnumConstant("PAPER")
+ .build()
+```
+
+To generate this:
+
+```kotlin
+enum class Roshambo {
+ ROCK,
+
+ SCISSORS,
+
+ PAPER
+}
+```
+
+Fancy enums are supported, where the enum values override methods or call a superclass constructor.
+Here's a comprehensive example:
+
+```kotlin
+val helloWorld = TypeSpec.enumBuilder("Roshambo")
+ .primaryConstructor(
+ FunSpec.constructorBuilder()
+ .addParameter("handsign", String::class)
+ .build()
+ )
+ .addEnumConstant(
+ "ROCK", TypeSpec.anonymousClassBuilder()
+ .addSuperclassConstructorParameter("%S", "fist")
+ .addFunction(
+ FunSpec.builder("toString")
+ .addModifiers(KModifier.OVERRIDE)
+ .addStatement("return %S", "avalanche!")
+ .returns(String::class)
+ .build()
+ )
+ .build()
+ )
+ .addEnumConstant(
+ "SCISSORS", TypeSpec.anonymousClassBuilder()
+ .addSuperclassConstructorParameter("%S", "peace")
+ .build()
+ )
+ .addEnumConstant(
+ "PAPER", TypeSpec.anonymousClassBuilder()
+ .addSuperclassConstructorParameter("%S", "flat")
+ .build()
+ )
+ .addProperty(
+ PropertySpec.builder("handsign", String::class, KModifier.PRIVATE)
+ .initializer("handsign")
+ .build()
+ )
+ .build()
+```
+
+Which generates this:
+
+```kotlin
+enum class Roshambo(private val handsign: String) {
+ ROCK("fist") {
+ override fun toString(): String = "avalanche!"
+ },
+
+ SCISSORS("peace"),
+
+ PAPER("flat");
+}
+```
diff --git a/docs/functions.md b/docs/functions.md
new file mode 100644
index 0000000..f24d19d
--- /dev/null
+++ b/docs/functions.md
@@ -0,0 +1,160 @@
+Functions
+=========
+
+All of the above functions have a code body. Use `KModifier.ABSTRACT` to get a function without any
+body. This is only legal if it is enclosed by an abstract class or an interface.
+
+```kotlin
+val flux = FunSpec.builder("flux")
+ .addModifiers(KModifier.ABSTRACT, KModifier.PROTECTED)
+ .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+ .addModifiers(KModifier.ABSTRACT)
+ .addFunction(flux)
+ .build()
+```
+
+Which generates this:
+
+```kotlin
+abstract class HelloWorld {
+ protected abstract fun flux()
+}
+```
+
+The other modifiers work where permitted.
+
+Methods also have parameters, varargs, KDoc, annotations, type variables, return type and receiver
+type for extension functions. All of these are configured with `FunSpec.Builder`.
+
+## Extension functions
+
+Extension functions can be generated by specifying a `receiver`.
+
+```kotlin
+val square = FunSpec.builder("square")
+ .receiver(Int::class)
+ .returns(Int::class)
+ .addStatement("var s = this * this")
+ .addStatement("return s")
+ .build()
+```
+
+Which outputs:
+
+```kotlin
+fun Int.square(): Int {
+ val s = this * this
+ return s
+}
+```
+
+## Single-expression functions
+
+KotlinPoet can recognize single-expression functions and print them out properly. It treats
+each function with a body that starts with `return` as a single-expression function:
+
+```kotlin
+val abs = FunSpec.builder("abs")
+ .addParameter("x", Int::class)
+ .returns(Int::class)
+ .addStatement("return if (x < 0) -x else x")
+ .build()
+```
+
+Which outputs:
+
+```kotlin
+fun abs(x: Int): Int = if (x < 0) -x else x
+```
+
+## Default function arguments
+
+Consider the example below.
+Function argument `b` has a default value of 0 to avoid overloading this function.
+
+```kotlin
+fun add(a: Int, b: Int = 0) {
+ print("a + b = ${a + b}")
+}
+```
+
+Use the `defaultValue()` builder function to declare default value for a function argument.
+
+```kotlin
+FunSpec.builder("add")
+ .addParameter("a", Int::class)
+ .addParameter(
+ ParameterSpec.builder("b", Int::class)
+ .defaultValue("%L", 0)
+ .build()
+ )
+ .addStatement("print(\"a + b = ${a + b}\")")
+ .build()
+```
+
+## Spaces wrap by default!
+
+In order to provide meaningful formatting, KotlinPoet would replace spaces, found in blocks of code,
+with new line symbols, in cases when the line of code exceeds the length limit. Let's take this
+function for example:
+
+```kotlin
+val funSpec = FunSpec.builder("foo")
+ .addStatement("return (100..10000).map { number -> number * number }.map { number -> number.toString() }.also { string -> println(string) }")
+ .build()
+```
+
+Depending on where it's found in the file, it may end up being printed out like this:
+
+```kotlin
+fun foo() = (100..10000).map { number -> number * number }.map { number -> number.toString() }.also
+{ string -> println(string) }
+```
+
+Unfortunately this code is broken: the compiler expects `also` and `{` to be on the same line.
+KotlinPoet is unable to understand the context of the expression and fix the formatting for you, but
+there's a trick you can use to declare a non-breaking space - use the `·` symbol where you would
+otherwise use a space. Let's apply this to our example:
+
+```kotlin
+val funSpec = FunSpec.builder("foo")
+ .addStatement("return (100..10000).map·{ number -> number * number }.map·{ number -> number.toString() }.also·{ string -> println(string) }")
+ .build()
+```
+
+This will now produce the following result:
+
+```kotlin
+fun foo() = (100..10000).map { number -> number * number }.map { number ->
+ number.toString()
+}.also { string -> println(string) }
+```
+
+The code is now correct and will compile properly. It still doesn't look perfect - you can play with
+replacing other spaces in the code block with `·` symbols to achieve better formatting.
+
+Another common use case where you'd want to ensure spaces don't wrap is when emitting string
+literals:
+
+```kotlin
+CodeBlock.of("""println("Class: $className")""")
+```
+
+If `$className` is long, KotlinPoet may wrap the space that precedes it, resulting in broken output:
+
+```kotlin
+println("Class:
+very.long.class.name.Here")
+```
+
+KotlinPoet doesn't know that `"Class: $className"` is, in fact, a string literal, and that the space
+inside of it should never be wrapped. To make sure this case is handled correctly, use the `%S`
+modifier (as described in [%S for Strings](s-for-strings.md)):
+
+```kotlin
+CodeBlock.of("""println(%S)""", "Class: $className")
+```
+
+Now the library knows it's dealing with a string literal and can use appropriate line-wrapping rules.
diff --git a/docs/index.md b/docs/index.md
index 683f40e..27db6f3 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -7,7 +7,7 @@
with metadata files (e.g., database schemas, protocol formats). By generating code, you eliminate
the need to write boilerplate while also keeping a single source of truth for the metadata.
-### Example
+## Example
Here's a `HelloWorld` file:
@@ -62,1470 +62,8 @@
**Note:** In order to maximize portability, KotlinPoet generates code with explicit visibility
modifiers. This ensures compatibility with both standard Kotlin projects as well as projects
-using [explicit API mode](https://kotlinlang.org/docs/whatsnew14.html#explicit-api-mode-for-library-authors).
-Examples in this file omit those modifiers for brevity.
-
-### Code & Control Flow
-
-Most of KotlinPoet's API uses immutable Kotlin objects. There's also builders, method chaining
-and varargs to make the API friendly. KotlinPoet offers models for Kotlin files (`FileSpec`),
-classes, interfaces & objects (`TypeSpec`), type aliases (`TypeAliasSpec`),
-properties (`PropertySpec`), functions & constructors (`FunSpec`), parameters (`ParameterSpec`) and
-annotations (`AnnotationSpec`).
-
-But the _body_ of methods and constructors is not modeled. There's no expression class, no
-statement class or syntax tree nodes. Instead, KotlinPoet uses strings for code blocks, and you can
-take advantage of Kotlin's multiline strings to make this look nice:
-
-```kotlin
-val main = FunSpec.builder("main")
- .addCode("""
- |var total = 0
- |for (i in 0..<10) {
- | total += i
- |}
- |""".trimMargin())
- .build()
-```
-
-Which generates this:
-
-```kotlin
-fun main() {
- var total = 0
- for (i in 0..<10) {
- total += i
- }
-}
-```
-
-There are additional APIs to assist with newlines, braces and indentation:
-
-```kotlin
-val main = FunSpec.builder("main")
- .addStatement("var total = 0")
- .beginControlFlow("for (i in 0..<10)")
- .addStatement("total += i")
- .endControlFlow()
- .build()
-```
-
-This example is lame because the generated code is constant! Suppose instead of just adding 0 to 10,
-we want to make the operation and range configurable. Here's a method that generates a method:
-
-```kotlin
-private fun computeRange(name: String, from: Int, to: Int, op: String): FunSpec {
- return FunSpec.builder(name)
- .returns(Int::class)
- .addStatement("var result = 1")
- .beginControlFlow("for (i in $from..<$to)")
- .addStatement("result = result $op i")
- .endControlFlow()
- .addStatement("return result")
- .build()
-}
-```
-
-And here's what we get when we call `computeRange("multiply10to20", 10, 20, "*")`:
-
-```kotlin
-fun multiply10to20(): kotlin.Int {
- var result = 1
- for (i in 10..<20) {
- result = result * i
- }
- return result
-}
-```
-
-Methods generating methods! And since KotlinPoet generates source instead of bytecode, you can
-read through it to make sure it's right.
-
-### %S for Strings
-
-When emitting code that includes string literals, we can use **`%S`** to emit a **string**, complete
-with wrapping quotation marks and escaping. Here's a program that emits 3 methods, each of which
-returns its own name:
-
-```kotlin
-fun main(args: Array<String>) {
- val helloWorld = TypeSpec.classBuilder("HelloWorld")
- .addFunction(whatsMyNameYo("slimShady"))
- .addFunction(whatsMyNameYo("eminem"))
- .addFunction(whatsMyNameYo("marshallMathers"))
- .build()
-
- val kotlinFile = FileSpec.builder("com.example.helloworld", "HelloWorld")
- .addType(helloWorld)
- .build()
-
- kotlinFile.writeTo(System.out)
-}
-
-private fun whatsMyNameYo(name: String): FunSpec {
- return FunSpec.builder(name)
- .returns(String::class)
- .addStatement("return %S", name)
- .build()
-}
-```
-
-In this case, using `%S` gives us quotation marks:
-
-```kotlin
-class HelloWorld {
- fun slimShady(): String = "slimShady"
-
- fun eminem(): String = "eminem"
-
- fun marshallMathers(): String = "marshallMathers"
-}
-```
-
-### %P for String Templates
-
-`%S` also handles the escaping of dollar signs (`$`), to avoid inadvertent creation of string
-templates, which may fail to compile in generated code:
-
-```kotlin
-val stringWithADollar = "Your total is " + "$" + "50"
-val funSpec = FunSpec.builder("printTotal")
- .returns(String::class)
- .addStatement("return %S", stringWithADollar)
- .build()
-```
-
-produces:
-
-```kotlin
-fun printTotal(): String = "Your total is ${'$'}50"
-```
-
-If you need to generate string templates, use `%P`, which doesn't escape dollars:
-
-```kotlin
-val amount = 50
-val stringWithADollar = "Your total is " + "$" + "amount"
-val funSpec = FunSpec.builder("printTotal")
- .returns(String::class)
- .addStatement("return %P", stringWithADollar)
- .build()
-```
-
-produces:
-
-```kotlin
-fun printTotal(): String = "Your total is $amount"
-```
-
-You can also use `CodeBlock`s as arguments to `%P`, which is handy when you need to reference
-importable types or members inside the string template:
-
-```kotlin
-val file = FileSpec.builder("com.example", "Digits")
- .addFunction(
- FunSpec.builder("print")
- .addParameter("digits", IntArray::class)
- .addStatement("println(%P)", buildCodeBlock {
- val contentToString = MemberName("kotlin.collections", "contentToString")
- add("These are the digits: \${digits.%M()}", contentToString)
- })
- .build()
- )
- .build()
-println(file)
-```
-
-The snippet above will produce the following output, handling the imports properly:
-
-```kotlin
-package com.example
-
-import kotlin.IntArray
-import kotlin.collections.contentToString
-
-fun print(digits: IntArray) {
- println("""These are the digits: ${digits.contentToString()}""")
-}
-```
-
-### %T for Types
-
-KotlinPoet has rich built-in support for types, including automatic generation of `import`
-statements. Just use **`%T`** to reference **types**:
-
-```kotlin
-val today = FunSpec.builder("today")
- .returns(Date::class)
- .addStatement("return %T()", Date::class)
- .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
- .addFunction(today)
- .build()
-
-val kotlinFile = FileSpec.builder("com.example.helloworld", "HelloWorld")
- .addType(helloWorld)
- .build()
-
-kotlinFile.writeTo(System.out)
-```
-
-That generates the following `.kt` file, complete with the necessary `import`:
-
-```kotlin
-package com.example.helloworld
-
-import java.util.Date
-
-class HelloWorld {
- fun today(): Date = Date()
-}
-```
-
-We passed `Date::class` to reference a class that just-so-happens to be available when we're
-generating code. This doesn't need to be the case. Here's a similar example, but this one
-references a class that doesn't exist (yet):
-
-```kotlin
-val hoverboard = ClassName("com.mattel", "Hoverboard")
-
-val tomorrow = FunSpec.builder("tomorrow")
- .returns(hoverboard)
- .addStatement("return %T()", hoverboard)
- .build()
-```
-
-And that not-yet-existent class is imported as well:
-
-```kotlin
-package com.example.helloworld
-
-import com.mattel.Hoverboard
-
-class HelloWorld {
- fun tomorrow(): Hoverboard = Hoverboard()
-}
-```
-
-The `ClassName` type is very important, and you'll need it frequently when you're using KotlinPoet.
-It can identify any _declared_ class. Declared types are just the beginning of Kotlin's rich type
-system: we also have arrays, parameterized types, wildcard types, lambda types and type variables.
-KotlinPoet has classes for building each of these:
-
-```kotlin
-import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
-import com.squareup.kotlinpoet.STAR
-
-val hoverboard = ClassName("com.mattel", "Hoverboard")
-val list = ClassName("kotlin.collections", "List")
-val arrayList = ClassName("kotlin.collections", "ArrayList")
-val listOfHoverboards = list.parameterizedBy(hoverboard)
-val arrayListOfHoverboards = arrayList.parameterizedBy(hoverboard)
-
-val thing = ClassName("com.misc", "Thing")
-val array = ClassName("kotlin", "Array")
-val producerArrayOfThings = array.parameterizedBy(WildcardTypeName.producerOf(thing))
-
-val beyond = FunSpec.builder("beyond")
- .returns(listOfHoverboards)
- .addStatement("val result = %T()", arrayListOfHoverboards)
- .addStatement("result += %T()", hoverboard)
- .addStatement("result += %T()", hoverboard)
- .addStatement("result += %T()", hoverboard)
- .addStatement("return result")
- .build()
-
-val printThings = FunSpec.builder("printThings")
- .addParameter("things", producerArrayOfThings)
- .addStatement("println(things)")
- .build()
-
-val printKClass = FunSpec.builder("printKClass")
- .addParameter("kClass", KClass::class.asClassName().parameterizedBy(STAR))
- .addStatement("println(kClass)")
- .build()
-```
-
-The `STAR` is represented as `*` in KotlinPoet. You can find more in the [KDoc][kdoc].
-
-KotlinPoet will decompose each type and import its components where possible.
-
-```kotlin
-package com.example.helloworld
-
-import com.mattel.Hoverboard
-import com.misc.Thing
-import kotlin.Array
-import kotlin.collections.ArrayList
-import kotlin.collections.List
-import kotlin.reflect.KClass
-
-class HelloWorld {
- fun beyond(): List<Hoverboard> {
- val result = ArrayList<Hoverboard>()
- result += Hoverboard()
- result += Hoverboard()
- result += Hoverboard()
- return result
- }
-
- fun printThings(things: Array<out Thing>) {
- println(things)
- }
-
- fun printKClass(kClass: KClass<*>) {
- println(kClass)
- }
-}
-```
-
-#### Nullable Types
-
-KotlinPoet supports nullable types. To convert a `TypeName` into its nullable counterpart, use the
-`copy()` method with `nullable` parameter set to `true`:
-
-```kotlin
-val java = PropertySpec.builder("java", String::class.asTypeName().copy(nullable = true))
- .mutable()
- .addModifiers(KModifier.PRIVATE)
- .initializer("null")
- .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
- .addProperty(java)
- .addProperty("kotlin", String::class, KModifier.PRIVATE)
- .build()
-```
-
-generates:
-
-```kotlin
-class HelloWorld {
- private var java: String? = null
-
- private val kotlin: String
-}
-```
-
-### %M for Members
-
-Similar to types, KotlinPoet has a special placeholder for **members** (functions and properties),
-which comes handy when your code needs to access top-level members and members declared inside
-objects. Use **`%M`** to reference members, pass an instance of `MemberName` as the argument for the
-placeholder, and KotlinPoet will handle imports automatically:
-
-```kotlin
-val createTaco = MemberName("com.squareup.tacos", "createTaco")
-val isVegan = MemberName("com.squareup.tacos", "isVegan")
-val file = FileSpec.builder("com.squareup.example", "TacoTest")
- .addFunction(
- FunSpec.builder("main")
- .addStatement("val taco = %M()", createTaco)
- .addStatement("println(taco.%M)", isVegan)
- .build()
- )
- .build()
-println(file)
-```
-
-The code above generates the following file:
-
-```kotlin
-package com.squareup.example
-
-import com.squareup.tacos.createTaco
-import com.squareup.tacos.isVegan
-
-fun main() {
- val taco = createTaco()
- println(taco.isVegan)
-}
-```
-
-As you can see, it's also possible to use `%M` to reference extension functions and properties. You
-just need to make sure the member can be imported without simple name collisions, otherwise
-importing will fail and the code generator output will not pass compilation. There's a way to work
-around such cases though - use `FileSpec.addAliasedImport()` to create an alias for a clashing
-`MemberName`:
-
-```kotlin
-val createTaco = MemberName("com.squareup.tacos", "createTaco")
-val createCake = MemberName("com.squareup.cakes", "createCake")
-val isTacoVegan = MemberName("com.squareup.tacos", "isVegan")
-val isCakeVegan = MemberName("com.squareup.cakes", "isVegan")
-val file = FileSpec.builder("com.squareup.example", "Test")
- .addAliasedImport(isTacoVegan, "isTacoVegan")
- .addAliasedImport(isCakeVegan, "isCakeVegan")
- .addFunction(
- FunSpec.builder("main")
- .addStatement("val taco = %M()", createTaco)
- .addStatement("val cake = %M()", createCake)
- .addStatement("println(taco.%M)", isTacoVegan)
- .addStatement("println(cake.%M)", isCakeVegan)
- .build()
- )
- .build()
-println(file)
-```
-
-KotlinPoet will produce an aliased import for `com.squareup.tacos2.isVegan`:
-
-```kotlin
-package com.squareup.example
-
-import com.squareup.cakes.createCake
-import com.squareup.tacos.createTaco
-import com.squareup.cakes.isVegan as isCakeVegan
-import com.squareup.tacos.isVegan as isTacoVegan
-
-fun main() {
- val taco = createTaco()
- val cake = createCake()
- println(taco.isTacoVegan)
- println(cake.isCakeVegan)
-}
-```
-
-#### MemberName and operators
-
-MemberName also supports operators, you can use `MemberName(String, KOperator)`
-or `MemberName(ClassName, KOperator)` to import and reference operators.
-
-```kotlin
-val taco = ClassName("com.squareup.tacos", "Taco")
-val meat = ClassName("com.squareup.tacos.ingredient", "Meat")
-val iterator = MemberName("com.squareup.tacos.internal", KOperator.ITERATOR)
-val minusAssign = MemberName("com.squareup.tacos.internal", KOperator.MINUS_ASSIGN)
-val file = FileSpec.builder("com.example", "Test")
- .addFunction(
- FunSpec.builder("makeTacoHealthy")
- .addParameter("taco", taco)
- .beginControlFlow("for (ingredient %M taco)", iterator)
- .addStatement("if (ingredient is %T) taco %M ingredient", meat, minusAssign)
- .endControlFlow()
- .addStatement("return taco")
- .build()
- )
- .build()
-println(file)
-```
-
-KotlinPoet will import the extension operator functions and emit the operator.
-
-```kotlin
-package com.example
-
-import com.squareup.tacos.Taco
-import com.squareup.tacos.ingredient.Meat
-import com.squareup.tacos.internal.iterator
-import com.squareup.tacos.internal.minusAssign
-
-fun makeTacoHealthy(taco: Taco) {
- for (ingredient in taco) {
- if (ingredient is Meat) taco -= ingredient
- }
- return taco
-}
-
-```
-
-### %N for Names
-
-Generated code is often self-referential. Use **`%N`** to refer to another generated declaration by
-its name. Here's a method that calls another:
-
-```kotlin
-fun byteToHex(b: Int): String {
- val result = CharArray(2)
- result[0] = hexDigit((b ushr 4) and 0xf)
- result[1] = hexDigit(b and 0xf)
- return String(result)
-}
-
-fun hexDigit(i: Int): Char {
- return (if (i < 10) i + '0'.toInt() else i - 10 + 'a'.toInt()).toChar()
-}
-```
-
-When generating the code above, we pass the `hexDigit()` method as an argument to the `byteToHex()`
-method using `%N`:
-
-```kotlin
-val hexDigit = FunSpec.builder("hexDigit")
- .addParameter("i", Int::class)
- .returns(Char::class)
- .addStatement("return (if (i < 10) i + '0'.toInt() else i - 10 + 'a'.toInt()).toChar()")
- .build()
-
-val byteToHex = FunSpec.builder("byteToHex")
- .addParameter("b", Int::class)
- .returns(String::class)
- .addStatement("val result = CharArray(2)")
- .addStatement("result[0] = %N((b ushr 4) and 0xf)", hexDigit)
- .addStatement("result[1] = %N(b and 0xf)", hexDigit)
- .addStatement("return String(result)")
- .build()
-```
-
-Another handy feature that `%N` provides is automatically escaping names that contain illegal
-identifier characters with double ticks. Suppose your code creates a `MemberName` with a Kotlin
-keyword as the simple name:
-
-```kotlin
-val taco = ClassName("com.squareup.tacos", "Taco")
-val packager = ClassName("com.squareup.tacos", "TacoPackager")
-val file = FileSpec.builder("com.example", "Test")
- .addFunction(
- FunSpec.builder("packageTacos")
- .addParameter("tacos", LIST.parameterizedBy(taco))
- .addParameter("packager", packager)
- .addStatement("packager.%N(tacos)", packager.member("package"))
- .build()
- )
- .build()
-```
-
-`%N` will escape the name for you, ensuring that the output will pass compilation:
-
-```kotlin
-package com.example
-
-import com.squareup.tacos.Taco
-import com.squareup.tacos.TacoPackager
-import kotlin.collections.List
-
-fun packageTacos(tacos: List<Taco>, packager: TacoPackager) {
- packager.`package`(tacos)
-}
-```
-
-### %L for Literals
-
-Although Kotlin's string templates usually work well in cases when you want to include literals into
-generated code, KotlinPoet offers additional syntax inspired-by but incompatible-with
-[`String.format()`][formatter]. It accepts **`%L`** to emit a **literal** value in the output. This
-works just like `Formatter`'s `%s`:
-
-```kotlin
-private fun computeRange(name: String, from: Int, to: Int, op: String): FunSpec {
- return FunSpec.builder(name)
- .returns(Int::class)
- .addStatement("var result = 0")
- .beginControlFlow("for (i in %L..<%L)", from, to)
- .addStatement("result = result %L i", op)
- .endControlFlow()
- .addStatement("return result")
- .build()
-}
-```
-
-Literals are emitted directly to the output code with no escaping. Arguments for literals may be
-strings, primitives, and a few KotlinPoet types described below.
-
-### Code block format strings
-
-Code blocks may specify the values for their placeholders in a few ways. Only one style may be used
-for each operation on a code block.
-
-#### Relative Arguments
-
-Pass an argument value for each placeholder in the format string to `CodeBlock.add()`. In each
-example, we generate code to say "I ate 3 tacos"
-
-```kotlin
-CodeBlock.builder().add("I ate %L %L", 3, "tacos")
-```
-
-#### Positional Arguments
-
-Place an integer index (1-based) before the placeholder in the format string to specify which
-argument to use.
-
-```kotlin
-CodeBlock.builder().add("I ate %2L %1L", "tacos", 3)
-```
-
-#### Named Arguments
-
-Use the syntax `%argumentName:X` where `X` is the format character and call `CodeBlock.addNamed()`
-with a map containing all argument keys in the format string. Argument names use characters in
-`a-z`, `A-Z`, `0-9`, and `_`, and must start with a lowercase character.
-
-```kotlin
-val map = LinkedHashMap<String, Any>()
-map += "food" to "tacos"
-map += "count" to 3
-CodeBlock.builder().addNamed("I ate %count:L %food:L", map)
-```
-
-### Functions
-
-All of the above functions have a code body. Use `KModifier.ABSTRACT` to get a function without any
-body. This is only legal if it is enclosed by an abstract class or an interface.
-
-```kotlin
-val flux = FunSpec.builder("flux")
- .addModifiers(KModifier.ABSTRACT, KModifier.PROTECTED)
- .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
- .addModifiers(KModifier.ABSTRACT)
- .addFunction(flux)
- .build()
-```
-
-Which generates this:
-
-```kotlin
-abstract class HelloWorld {
- protected abstract fun flux()
-}
-```
-
-The other modifiers work where permitted.
-
-Methods also have parameters, varargs, KDoc, annotations, type variables, return type and receiver
-type for extension functions. All of these are configured with `FunSpec.Builder`.
-
-#### Extension functions
-
-Extension functions can be generated by specifying a `receiver`.
-
-```kotlin
-val square = FunSpec.builder("square")
- .receiver(Int::class)
- .returns(Int::class)
- .addStatement("var s = this * this")
- .addStatement("return s")
- .build()
-```
-
-Which outputs:
-
-```kotlin
-fun Int.square(): Int {
- val s = this * this
- return s
-}
-```
-
-#### Single-expression functions
-
-KotlinPoet can recognize single-expression functions and print them out properly. It treats
-each function with a body that starts with `return` as a single-expression function:
-
-```kotlin
-val abs = FunSpec.builder("abs")
- .addParameter("x", Int::class)
- .returns(Int::class)
- .addStatement("return if (x < 0) -x else x")
- .build()
-```
-
-Which outputs:
-
-```kotlin
-fun abs(x: Int): Int = if (x < 0) -x else x
-```
-
-#### Default function arguments
-
-Consider the example below.
-Function argument `b` has a default value of 0 to avoid overloading this function.
-
-```kotlin
-fun add(a: Int, b: Int = 0) {
- print("a + b = ${a + b}")
-}
-```
-
-Use the `defaultValue()` builder function to declare default value for a function argument.
-
-```kotlin
-FunSpec.builder("add")
- .addParameter("a", Int::class)
- .addParameter(
- ParameterSpec.builder("b", Int::class)
- .defaultValue("%L", 0)
- .build()
- )
- .addStatement("print(\"a + b = ${a + b}\")")
- .build()
-```
-
-#### Spaces wrap by default!
-
-In order to provide meaningful formatting, KotlinPoet would replace spaces, found in blocks of code,
-with new line symbols, in cases when the line of code exceeds the length limit. Let's take this
-function for example:
-
-```kotlin
-val funSpec = FunSpec.builder("foo")
- .addStatement("return (100..10000).map { number -> number * number }.map { number -> number.toString() }.also { string -> println(string) }")
- .build()
-```
-
-Depending on where it's found in the file, it may end up being printed out like this:
-
-```kotlin
-fun foo() = (100..10000).map { number -> number * number }.map { number -> number.toString() }.also
-{ string -> println(string) }
-```
-
-Unfortunately this code is broken: the compiler expects `also` and `{` to be on the same line.
-KotlinPoet is unable to understand the context of the expression and fix the formatting for you, but
-there's a trick you can use to declare a non-breaking space - use the `·` symbol where you would
-otherwise use a space. Let's apply this to our example:
-
-```kotlin
-val funSpec = FunSpec.builder("foo")
- .addStatement("return (100..10000).map·{ number -> number * number }.map·{ number -> number.toString() }.also·{ string -> println(string) }")
- .build()
-```
-
-This will now produce the following result:
-
-```kotlin
-fun foo() = (100..10000).map { number -> number * number }.map { number ->
- number.toString()
-}.also { string -> println(string) }
-```
-
-The code is now correct and will compile properly. It still doesn't look perfect - you can play with
-replacing other spaces in the code block with `·` symbols to achieve better formatting.
-
-Another common use case where you'd want to ensure spaces don't wrap is when emitting string literals:
-
-```kotlin
-CodeBlock.of("""println("Class: $className")""")
-```
-
-If `$className` is long, KotlinPoet may wrap the space that precedes it, resulting in broken output:
-
-```kotlin
-println("Class:
-very.long.class.name.Here")
-```
-
-KotlinPoet doesn't know that `"Class: $className"` is, in fact, a string literal, and that the space inside of it
-should never be wrapped. To make sure this case is handled correctly, use the `%S` modifier (as described in
-[%S for Strings](#s-for-strings)):
-
-```kotlin
-CodeBlock.of("""println(%S)""", "Class: $className")
-```
-
-Now the library knows it's dealing with a string literal and can use appropriate line-wrapping rules.
-
-### Constructors
-
-`FunSpec` is a slight misnomer; it can also be used for constructors:
-
-```kotlin
-val flux = FunSpec.constructorBuilder()
- .addParameter("greeting", String::class)
- .addStatement("this.%N = %N", "greeting", "greeting")
- .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
- .addProperty("greeting", String::class, KModifier.PRIVATE)
- .addFunction(flux)
- .build()
-```
-
-Which generates this:
-
-```kotlin
-class HelloWorld {
- private val greeting: String
-
- constructor(greeting: String) {
- this.greeting = greeting
- }
-}
-```
-
-For the most part, constructors work just like methods. When emitting code, KotlinPoet will place
-constructors before methods in the output file.
-
-Often times you'll need to generate the primary constructor for a class:
-
-```kotlin
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
- .primaryConstructor(flux)
- .addProperty("greeting", String::class, KModifier.PRIVATE)
- .build()
-```
-
-This code, however, generates the following:
-
-```kotlin
-class HelloWorld(greeting: String) {
- private val greeting: String
-
- init {
- this.greeting = greeting
- }
-}
-```
-
-By default, KotlinPoet won't merge primary constructor parameters and properties, even if they share
-the same name. To achieve the effect, you have to tell KotlinPoet that the property is initialized
-via the constructor parameter:
-
-```kotlin
-val flux = FunSpec.constructorBuilder()
- .addParameter("greeting", String::class)
- .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
- .primaryConstructor(flux)
- .addProperty(
- PropertySpec.builder("greeting", String::class)
- .initializer("greeting")
- .addModifiers(KModifier.PRIVATE)
- .build()
- )
- .build()
-```
-
-Now we're getting the following output:
-
-```kotlin
-class HelloWorld(private val greeting: String)
-```
-
-Notice that KotlinPoet omits `{}` for classes with empty bodies.
-
-### Parameters
-
-Declare parameters on methods and constructors with either `ParameterSpec.builder()` or
-`FunSpec`'s convenient `addParameter()` API:
-
-```kotlin
-val android = ParameterSpec.builder("android", String::class)
- .defaultValue("\"pie\"")
- .build()
-
-val welcomeOverlords = FunSpec.builder("welcomeOverlords")
- .addParameter(android)
- .addParameter("robot", String::class)
- .build()
-```
-
-The code above generates:
-
-```kotlin
-fun welcomeOverlords(android: String = "pie", robot: String) {
-}
-```
-
-The extended `Builder` form is necessary when the parameter has annotations (such as `@Inject`).
-
-### Properties
-
-Like parameters, properties can be created either with builders or by using convenient helper
-methods:
-
-```kotlin
-val android = PropertySpec.builder("android", String::class)
- .addModifiers(KModifier.PRIVATE)
- .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
- .addProperty(android)
- .addProperty("robot", String::class, KModifier.PRIVATE)
- .build()
-```
-
-Which generates:
-
-```kotlin
-class HelloWorld {
- private val android: String
-
- private val robot: String
-}
-```
-
-The extended `Builder` form is necessary when a field has KDoc, annotations, or a field
-initializer. Field initializers use the same [`String.format()`][formatter]-like syntax as the code
-blocks above:
-
-```kotlin
-val android = PropertySpec.builder("android", String::class)
- .addModifiers(KModifier.PRIVATE)
- .initializer("%S + %L", "Oreo v.", 8.1)
- .build()
-```
-
-Which generates:
-
-```kotlin
-private val android: String = "Oreo v." + 8.1
-```
-
-By default `PropertySpec.Builder` produces `val` properties. Use `mutable()` if you need a
-`var`:
-
-```kotlin
-val android = PropertySpec.builder("android", String::class)
- .mutable()
- .addModifiers(KModifier.PRIVATE)
- .initializer("%S + %L", "Oreo v.", 8.1)
- .build()
-```
-
-#### Inline properties
-
-The way KotlinPoet models inline properties deserves special mention. The following snippet of code:
-
-```kotlin
-val android = PropertySpec.builder("android", String::class)
- .mutable()
- .addModifiers(KModifier.INLINE)
- .build()
-```
-
-will produce an error:
-
-```
-java.lang.IllegalArgumentException: KotlinPoet doesn't allow setting the inline modifier on
-properties. You should mark either the getter, the setter, or both inline.
-```
-
-Indeed, a property marked with `inline` should have at least one accessor which will be inlined by
-the compiler. Let's add a getter to this property:
-
-```kotlin
-val android = PropertySpec.builder("android", String::class)
- .mutable()
- .getter(
- FunSpec.getterBuilder()
- .addModifiers(KModifier.INLINE)
- .addStatement("return %S", "foo")
- .build()
- )
- .build()
-```
-
-The result is the following:
-
-```kotlin
-var android: kotlin.String
- inline get() = "foo"
-```
-
-Now, what if we wanted to add a non-inline setter to the property above? We can do so without
-modifying any of the code we wrote previously:
-
-```kotlin
-val android = PropertySpec.builder("android", String::class)
- .mutable()
- .getter(
- FunSpec.getterBuilder()
- .addModifiers(KModifier.INLINE)
- .addStatement("return %S", "foo")
- .build()
- )
- .setter(
- FunSpec.setterBuilder()
- .addParameter("value", String::class)
- .build()
- )
- .build()
-```
-
-We get the expected result:
-
-```kotlin
-var android: kotlin.String
- inline get() = "foo"
- set(`value`) {
- }
-```
-
-Finally, if we go back and add `KModifier.INLINE` to the setter, KotlinPoet can wrap it nicely and
-produce the following result:
-
-```kotlin
-inline var android: kotlin.String
- get() = "foo"
- set(`value`) {
- }
-```
-
-Removing the modifier from either the getter or the setter will unwrap the expression back.
-
-If, on the other hand, KotlinPoet had allowed marking a property `inline` directly, the programmer
-would have had to manually add/remove the modifier whenever the state of the accessors changes in
-order to get correct and compilable output. We're solving this problem by making accessors the
-source of truth for the `inline` modifier.
-
-### Interfaces
-
-KotlinPoet has no trouble with interfaces. Note that interface methods must always be `ABSTRACT`.
-The modifier is necessary when defining the interface:
-
-```kotlin
-val helloWorld = TypeSpec.interfaceBuilder("HelloWorld")
- .addProperty("buzz", String::class)
- .addFunction(
- FunSpec.builder("beep")
- .addModifiers(KModifier.ABSTRACT)
- .build()
- )
- .build()
-```
-
-But these modifiers are omitted when the code is generated. These are the default so we don't need
-to include them for `kotlinc`'s benefit!
-
-```kotlin
-interface HelloWorld {
- val buzz: String
-
- fun beep()
-}
-```
-
-Kotlin 1.4 adds support for functional interfaces via `fun interface` syntax. To create this in
-KotlinPoet, use `TypeSpec.funInterfaceBuilder()`.
-
-```kotlin
-val helloWorld = TypeSpec.funInterfaceBuilder("HelloWorld")
- .addFunction(
- FunSpec.builder("beep")
- .addModifiers(KModifier.ABSTRACT)
- .build()
- )
- .build()
-
-// Generates...
-fun interface HelloWorld {
- fun beep()
-}
-```
-
-### Objects
-
-KotlinPoet supports objects:
-
-```kotlin
-val helloWorld = TypeSpec.objectBuilder("HelloWorld")
- .addProperty(
- PropertySpec.builder("buzz", String::class)
- .initializer("%S", "buzz")
- .build()
- )
- .addFunction(
- FunSpec.builder("beep")
- .addStatement("println(%S)", "Beep!")
- .build()
- )
- .build()
-```
-
-Similarly, you can create companion objects and add them to classes using `addType()`:
-
-```kotlin
-val companion = TypeSpec.companionObjectBuilder()
- .addProperty(
- PropertySpec.builder("buzz", String::class)
- .initializer("%S", "buzz")
- .build()
- )
- .addFunction(
- FunSpec.builder("beep")
- .addStatement("println(%S)", "Beep!")
- .build()
- )
- .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
- .addType(companion)
- .build()
-```
-
-You can provide an optional name for a companion object.
-
-### Enums
-
-Use `enumBuilder` to create the enum type, and `addEnumConstant()` for each value:
-
-```kotlin
-val helloWorld = TypeSpec.enumBuilder("Roshambo")
- .addEnumConstant("ROCK")
- .addEnumConstant("SCISSORS")
- .addEnumConstant("PAPER")
- .build()
-```
-
-To generate this:
-
-```kotlin
-enum class Roshambo {
- ROCK,
-
- SCISSORS,
-
- PAPER
-}
-```
-
-Fancy enums are supported, where the enum values override methods or call a superclass constructor.
-Here's a comprehensive example:
-
-```kotlin
-val helloWorld = TypeSpec.enumBuilder("Roshambo")
- .primaryConstructor(
- FunSpec.constructorBuilder()
- .addParameter("handsign", String::class)
- .build()
- )
- .addEnumConstant(
- "ROCK", TypeSpec.anonymousClassBuilder()
- .addSuperclassConstructorParameter("%S", "fist")
- .addFunction(
- FunSpec.builder("toString")
- .addModifiers(KModifier.OVERRIDE)
- .addStatement("return %S", "avalanche!")
- .returns(String::class)
- .build()
- )
- .build()
- )
- .addEnumConstant(
- "SCISSORS", TypeSpec.anonymousClassBuilder()
- .addSuperclassConstructorParameter("%S", "peace")
- .build()
- )
- .addEnumConstant(
- "PAPER", TypeSpec.anonymousClassBuilder()
- .addSuperclassConstructorParameter("%S", "flat")
- .build()
- )
- .addProperty(
- PropertySpec.builder("handsign", String::class, KModifier.PRIVATE)
- .initializer("handsign")
- .build()
- )
- .build()
-```
-
-Which generates this:
-
-```kotlin
-enum class Roshambo(private val handsign: String) {
- ROCK("fist") {
- override fun toString(): String = "avalanche!"
- },
-
- SCISSORS("peace"),
-
- PAPER("flat");
-}
-```
-
-### Anonymous Inner Classes
-
-In the enum code, we used `TypeSpec.anonymousClassBuilder()`. Anonymous inner classes can also be
-used in code blocks. They are values that can be referenced with `%L`:
-
-```kotlin
-val comparator = TypeSpec.anonymousClassBuilder()
- .addSuperinterface(Comparator::class.parameterizedBy(String::class))
- .addFunction(
- FunSpec.builder("compare")
- .addModifiers(KModifier.OVERRIDE)
- .addParameter("a", String::class)
- .addParameter("b", String::class)
- .returns(Int::class)
- .addStatement("return %N.length - %N.length", "a", "b")
- .build()
- )
- .build()
-
-val helloWorld = TypeSpec.classBuilder("HelloWorld")
- .addFunction(
- FunSpec.builder("sortByLength")
- .addParameter("strings", List::class.parameterizedBy(String::class))
- .addStatement("%N.sortedWith(%L)", "strings", comparator)
- .build()
- )
- .build()
-```
-
-This generates a method that contains a class that contains a method:
-
-```kotlin
-class HelloWorld {
- fun sortByLength(strings: List<String>) {
- strings.sortedWith(object : Comparator<String> {
- override fun compare(a: String, b: String): Int = a.length - b.length
- })
- }
-}
-```
-
-One particularly tricky part of defining anonymous inner classes is the arguments to the superclass
-constructor. To pass them use `TypeSpec.Builder`'s `addSuperclassConstructorParameter()` method.
-
-### Annotations
-
-Simple annotations are easy:
-
-```kotlin
-val test = FunSpec.builder("test string equality")
- .addAnnotation(Test::class)
- .addStatement("assertThat(%1S).isEqualTo(%1S)", "foo")
- .build()
-```
-
-Which generates this function with an `@Test` annotation:
-
-```kotlin
-@Test
-fun `test string equality`() {
- assertThat("foo").isEqualTo("foo")
-}
-```
-
-Use `AnnotationSpec.builder()` to set properties on annotations:
-
-```kotlin
-val logRecord = FunSpec.builder("recordEvent")
- .addModifiers(KModifier.ABSTRACT)
- .addAnnotation(
- AnnotationSpec.builder(Headers::class)
- .addMember("accept = %S", "application/json; charset=utf-8")
- .addMember("userAgent = %S", "Square Cash")
- .build()
- )
- .addParameter("logRecord", LogRecord::class)
- .returns(LogReceipt::class)
- .build()
-```
-
-Which generates this annotation with `accept` and `userAgent` properties:
-
-```kotlin
-@Headers(
- accept = "application/json; charset=utf-8",
- userAgent = "Square Cash"
-)
-abstract fun recordEvent(logRecord: LogRecord): LogReceipt
-```
-
-When you get fancy, annotation values can be annotations themselves. Use `%L` for embedded
-annotations:
-
-```kotlin
-val headerList = ClassName("", "HeaderList")
-val header = ClassName("", "Header")
-val logRecord = FunSpec.builder("recordEvent")
- .addModifiers(KModifier.ABSTRACT)
- .addAnnotation(
- AnnotationSpec.builder(headerList)
- .addMember(
- "[\n⇥%L,\n%L⇤\n]",
- AnnotationSpec.builder(header)
- .addMember("name = %S", "Accept")
- .addMember("value = %S", "application/json; charset=utf-8")
- .build(),
- AnnotationSpec.builder(header)
- .addMember("name = %S", "User-Agent")
- .addMember("value = %S", "Square Cash")
- .build()
- )
- .build()
- )
- .addParameter("logRecord", logRecordName)
- .returns(logReceipt)
- .build()
-```
-
-Which generates this:
-
-```kotlin
-@HeaderList(
- [
- Header(name = "Accept", value = "application/json; charset=utf-8"),
- Header(name = "User-Agent", value = "Square Cash")
- ]
-)
-abstract fun recordEvent(logRecord: LogRecord): LogReceipt
-```
-
-KotlinPoet supports use-site targets for annotations:
-
-```kotlin
-val utils = FileSpec.builder("com.example", "Utils")
- .addAnnotation(
- AnnotationSpec.builder(JvmName::class)
- .useSiteTarget(UseSiteTarget.FILE)
- .build()
- )
- .addFunction(
- FunSpec.builder("abs")
- .receiver(Int::class)
- .returns(Int::class)
- .addStatement("return if (this < 0) -this else this")
- .build()
- )
- .build()
-```
-
-Will output this:
-
-```kotlin
-@file:JvmName
-
-package com.example
-
-import kotlin.Int
-import kotlin.jvm.JvmName
-
-fun Int.abs(): Int = if (this < 0) -this else this
-```
-
-### Type Aliases
-
-KotlinPoet provides API for creating Type Aliases, which supports simple class names, parameterized
-types and lambdas:
-
-```kotlin
-val k = TypeVariableName("K")
-val t = TypeVariableName("T")
-
-val fileTable = Map::class.asClassName()
- .parameterizedBy(k, Set::class.parameterizedBy(File::class))
-
-val predicate = LambdaTypeName.get(
- parameters = arrayOf(t),
- returnType = Boolean::class.asClassName()
-)
-val helloWorld = FileSpec.builder("com.example", "HelloWorld")
- .addTypeAlias(TypeAliasSpec.builder("Word", String::class).build())
- .addTypeAlias(
- TypeAliasSpec.builder("FileTable", fileTable)
- .addTypeVariable(k)
- .build()
- )
- .addTypeAlias(
- TypeAliasSpec.builder("Predicate", predicate)
- .addTypeVariable(t)
- .build()
- )
- .build()
-```
-
-Which generates the following:
-
-```kotlin
-package com.example
-
-import java.io.File
-import kotlin.Boolean
-import kotlin.String
-import kotlin.collections.Map
-import kotlin.collections.Set
-
-typealias Word = String
-
-typealias FileTable<K> = Map<K, Set<File>>
-
-typealias Predicate<T> = (T) -> Boolean
-```
-
-### Callable References
-
-[Callable references](https://kotlinlang.org/docs/reference/reflection.html#callable-references) to
-constructors, functions, and properties may be emitted via:
-
-- `ClassName.constructorReference()` for constructors
-- `MemberName.reference()` for functions and properties
-
-For example,
-
-```kotlin
-val helloClass = ClassName("com.example.hello", "Hello")
-val worldFunction: MemberName = helloClass.member("world")
-val byeProperty: MemberName = helloClass.nestedClass("World").member("bye")
-
-val factoriesFun = FunSpec.builder("factories")
- .addStatement("val hello = %L", helloClass.constructorReference())
- .addStatement("val world = %L", worldFunction.reference())
- .addStatement("val bye = %L", byeProperty.reference())
- .build()
-
-FileSpec.builder("com.example", "HelloWorld")
- .addFunction(factoriesFun)
- .build()
-```
-
-would generate:
-
-```kotlin
-package com.example
-
-import com.example.hello.Hello
-
-fun factories() {
- val hello = ::Hello
- val world = Hello::world
- val bye = Hello.World::bye
-}
-```
-
-Top-level classes and members with conflicting names may require aliased imports, as with
-[member names](#m-for-members).
-
-kotlin-reflect
---------
-
-To generate source code from
-any [`KType`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-type/), including
-information that's not accessible to the builtin reflection APIs, KotlinPoet depends
-on [kotlin-reflect](https://kotlinlang.org/docs/reflection.html#jvm-dependency). `kotlin-reflect`
-can read the metadata of your classes and access this extra information. KotlinPoet can for an
-example, read the type parameters and
-their [variance](https://kotlinlang.org/docs/generics.html#variance) from a generic `KType` and
-generate appropriate source code.
-
-`kotlin-reflect` is a relatively big dependency though and in some cases it is desirable to remove
-it from the final executable to save some space and/or simplify the proguard/R8 setup (for example
-for a Gradle plugin that generates Kotlin code). It is possible to do so and still use most of the
-KotlinPoet APIs:
-
-```kotlin
-dependencies {
- implementation("com.squareup:kotlinpoet:<version>") {
- exclude(module = "kotlin-reflect")
- }
-}
-```
-
-The main APIs that require `kotlin-reflect`
-are [`KType.asTypeName()`](https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/as-type-name.html)
-and [`typeNameOf<T>()`](https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/type-name-of.html).
-If you're calling one of these without `kotlin-reflect` in the classpath and the type is generic
-or has annotations you will get a crash.
-
-You can replace it with code that passes type parameters or annotations explicitly and doesn't
-need `kotlin-reflect`. For example:
-
-```kotlin
-// Replace
-// kotlin-reflect needed
-val typeName = typeNameOf<List<Int?>>()
-
-// With
-// kotlin-reflect not needed
-val typeName =
- List::class.asClassName().parameterizedBy(Int::class.asClassName().copy(nullable = true))
-```
+using [explicit API mode][explicit-api]. Examples in the documentation omit those modifiers for
+brevity.
Download
--------
@@ -1537,7 +75,7 @@
```xml
<dependency>
<groupId>com.squareup</groupId>
- <artifactId>kotlinpoet</artifactId>
+ <artifactId>kotlinpoet-jvm</artifactId>
<version>[version]</version>
</dependency>
```
@@ -1550,7 +88,6 @@
Snapshots of the development version are available in [Sonatype's `snapshots` repository][snap].
-
License
-------
@@ -1568,10 +105,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-
- [dl]: https://search.maven.org/remote_content?g=com.squareup&a=kotlinpoet&v=LATEST
- [version-shield]: https://img.shields.io/maven-central/v/com.squareup/kotlinpoet
- [snap]: https://s01.oss.sonatype.org/content/repositories/snapshots/com/squareup/kotlinpoet/
[kdoc]: https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/
[javapoet]: https://github.com/square/javapoet/
- [formatter]: https://developer.android.com/reference/java/util/Formatter.html
+ [explicit-api]: https://kotlinlang.org/docs/whatsnew14.html#explicit-api-mode-for-library-authors
+ [version-shield]: https://img.shields.io/maven-central/v/com.squareup/kotlinpoet
+ [dl]: https://search.maven.org/remote_content?g=com.squareup&a=kotlinpoet-jvm&v=LATEST
+ [snap]: https://s01.oss.sonatype.org/content/repositories/snapshots/com/squareup/kotlinpoet/
diff --git a/docs/interfaces.md b/docs/interfaces.md
new file mode 100644
index 0000000..52682b4
--- /dev/null
+++ b/docs/interfaces.md
@@ -0,0 +1,45 @@
+Interfaces
+==========
+
+KotlinPoet has no trouble with interfaces. Note that interface methods must always be `ABSTRACT`.
+The modifier is necessary when defining the interface:
+
+```kotlin
+val helloWorld = TypeSpec.interfaceBuilder("HelloWorld")
+ .addProperty("buzz", String::class)
+ .addFunction(
+ FunSpec.builder("beep")
+ .addModifiers(KModifier.ABSTRACT)
+ .build()
+ )
+ .build()
+```
+
+But these modifiers are omitted when the code is generated. These are the default so we don't need
+to include them for `kotlinc`'s benefit!
+
+```kotlin
+interface HelloWorld {
+ val buzz: String
+
+ fun beep()
+}
+```
+
+Kotlin 1.4 adds support for functional interfaces via `fun interface` syntax. To create this in
+KotlinPoet, use `TypeSpec.funInterfaceBuilder()`.
+
+```kotlin
+val helloWorld = TypeSpec.funInterfaceBuilder("HelloWorld")
+ .addFunction(
+ FunSpec.builder("beep")
+ .addModifiers(KModifier.ABSTRACT)
+ .build()
+ )
+ .build()
+
+// Generates...
+fun interface HelloWorld {
+ fun beep()
+}
+```
diff --git a/docs/kotlin-reflect.md b/docs/kotlin-reflect.md
new file mode 100644
index 0000000..0bda13a
--- /dev/null
+++ b/docs/kotlin-reflect.md
@@ -0,0 +1,45 @@
+kotlin-reflect
+==============
+
+To generate source code from any [`KType`][k-type], including information that's not accessible to
+the builtin reflection APIs, KotlinPoet depends on [kotlin-reflect][kotlin-reflect]. `kotlin-reflect`
+can read the metadata of your classes and access this extra information. KotlinPoet can for an
+example, read the type parameters and their [variance][variance] from a generic `KType` and
+generate appropriate source code.
+
+`kotlin-reflect` is a relatively big dependency though and in some cases it is desirable to remove
+it from the final executable to save some space and/or simplify the proguard/R8 setup (for example
+for a Gradle plugin that generates Kotlin code). It is possible to do so and still use most of the
+KotlinPoet APIs:
+
+```kotlin
+dependencies {
+ implementation("com.squareup:kotlinpoet:<version>") {
+ exclude(module = "kotlin-reflect")
+ }
+}
+```
+
+The main APIs that require `kotlin-reflect` are [`KType.asTypeName()`][as-type-name] and
+[`typeNameOf<T>()`][type-name-of]. If you're calling one of these without `kotlin-reflect` in the
+classpath and the type is generic or has annotations you will get a crash.
+
+You can replace it with code that passes type parameters or annotations explicitly and doesn't
+need `kotlin-reflect`. For example:
+
+```kotlin
+// Replace
+// kotlin-reflect needed
+val typeName = typeNameOf<List<Int?>>()
+
+// With
+// kotlin-reflect not needed
+val typeName =
+ List::class.asClassName().parameterizedBy(Int::class.asClassName().copy(nullable = true))
+```
+
+ [k-type]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-type/
+ [kotlin-reflect]: https://kotlinlang.org/docs/reflection.html#jvm-dependency
+ [variance]: https://kotlinlang.org/docs/generics.html#variance
+ [as-type-name]: https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/as-type-name.html
+ [type-name-of]: https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/type-name-of.html
diff --git a/docs/l-for-literals.md b/docs/l-for-literals.md
new file mode 100644
index 0000000..82a0d0a
--- /dev/null
+++ b/docs/l-for-literals.md
@@ -0,0 +1,25 @@
+%L for Literals
+===============
+
+Although Kotlin's string templates usually work well in cases when you want to include literals into
+generated code, KotlinPoet offers additional syntax inspired-by but incompatible-with
+[`String.format()`][formatter]. It accepts **`%L`** to emit a **literal** value in the output. This
+works just like `Formatter`'s `%s`:
+
+```kotlin
+private fun computeRange(name: String, from: Int, to: Int, op: String): FunSpec {
+ return FunSpec.builder(name)
+ .returns(Int::class)
+ .addStatement("var result = 0")
+ .beginControlFlow("for (i in %L..<%L)", from, to)
+ .addStatement("result = result %L i", op)
+ .endControlFlow()
+ .addStatement("return result")
+ .build()
+}
+```
+
+Literals are emitted directly to the output code with no escaping. Arguments for literals may be
+strings, primitives, and a few KotlinPoet types described below.
+
+ [formatter]: https://developer.android.com/reference/java/util/Formatter.html
diff --git a/docs/m-for-members.md b/docs/m-for-members.md
new file mode 100644
index 0000000..0a683c7
--- /dev/null
+++ b/docs/m-for-members.md
@@ -0,0 +1,122 @@
+%M for Members
+==============
+
+Similar to types, KotlinPoet has a special placeholder for **members** (functions and properties),
+which comes handy when your code needs to access top-level members and members declared inside
+objects. Use **`%M`** to reference members, pass an instance of `MemberName` as the argument for the
+placeholder, and KotlinPoet will handle imports automatically:
+
+```kotlin
+val createTaco = MemberName("com.squareup.tacos", "createTaco")
+val isVegan = MemberName("com.squareup.tacos", "isVegan")
+val file = FileSpec.builder("com.squareup.example", "TacoTest")
+ .addFunction(
+ FunSpec.builder("main")
+ .addStatement("val taco = %M()", createTaco)
+ .addStatement("println(taco.%M)", isVegan)
+ .build()
+ )
+ .build()
+println(file)
+```
+
+The code above generates the following file:
+
+```kotlin
+package com.squareup.example
+
+import com.squareup.tacos.createTaco
+import com.squareup.tacos.isVegan
+
+fun main() {
+ val taco = createTaco()
+ println(taco.isVegan)
+}
+```
+
+As you can see, it's also possible to use `%M` to reference extension functions and properties. You
+just need to make sure the member can be imported without simple name collisions, otherwise
+importing will fail and the code generator output will not pass compilation. There's a way to work
+around such cases though - use `FileSpec.addAliasedImport()` to create an alias for a clashing
+`MemberName`:
+
+```kotlin
+val createTaco = MemberName("com.squareup.tacos", "createTaco")
+val createCake = MemberName("com.squareup.cakes", "createCake")
+val isTacoVegan = MemberName("com.squareup.tacos", "isVegan")
+val isCakeVegan = MemberName("com.squareup.cakes", "isVegan")
+val file = FileSpec.builder("com.squareup.example", "Test")
+ .addAliasedImport(isTacoVegan, "isTacoVegan")
+ .addAliasedImport(isCakeVegan, "isCakeVegan")
+ .addFunction(
+ FunSpec.builder("main")
+ .addStatement("val taco = %M()", createTaco)
+ .addStatement("val cake = %M()", createCake)
+ .addStatement("println(taco.%M)", isTacoVegan)
+ .addStatement("println(cake.%M)", isCakeVegan)
+ .build()
+ )
+ .build()
+println(file)
+```
+
+KotlinPoet will produce an aliased import for `com.squareup.tacos2.isVegan`:
+
+```kotlin
+package com.squareup.example
+
+import com.squareup.cakes.createCake
+import com.squareup.tacos.createTaco
+import com.squareup.cakes.isVegan as isCakeVegan
+import com.squareup.tacos.isVegan as isTacoVegan
+
+fun main() {
+ val taco = createTaco()
+ val cake = createCake()
+ println(taco.isTacoVegan)
+ println(cake.isCakeVegan)
+}
+```
+
+## MemberName and operators
+
+MemberName also supports operators, you can use `MemberName(String, KOperator)`
+or `MemberName(ClassName, KOperator)` to import and reference operators.
+
+```kotlin
+val taco = ClassName("com.squareup.tacos", "Taco")
+val meat = ClassName("com.squareup.tacos.ingredient", "Meat")
+val iterator = MemberName("com.squareup.tacos.internal", KOperator.ITERATOR)
+val minusAssign = MemberName("com.squareup.tacos.internal", KOperator.MINUS_ASSIGN)
+val file = FileSpec.builder("com.example", "Test")
+ .addFunction(
+ FunSpec.builder("makeTacoHealthy")
+ .addParameter("taco", taco)
+ .beginControlFlow("for (ingredient %M taco)", iterator)
+ .addStatement("if (ingredient is %T) taco %M ingredient", meat, minusAssign)
+ .endControlFlow()
+ .addStatement("return taco")
+ .build()
+ )
+ .build()
+println(file)
+```
+
+KotlinPoet will import the extension operator functions and emit the operator.
+
+```kotlin
+package com.example
+
+import com.squareup.tacos.Taco
+import com.squareup.tacos.ingredient.Meat
+import com.squareup.tacos.internal.iterator
+import com.squareup.tacos.internal.minusAssign
+
+fun makeTacoHealthy(taco: Taco) {
+ for (ingredient in taco) {
+ if (ingredient is Meat) taco -= ingredient
+ }
+ return taco
+}
+
+```
diff --git a/docs/n-for-names.md b/docs/n-for-names.md
new file mode 100644
index 0000000..cc3783a
--- /dev/null
+++ b/docs/n-for-names.md
@@ -0,0 +1,70 @@
+%N for Names
+============
+
+Generated code is often self-referential. Use **`%N`** to refer to another generated declaration by
+its name. Here's a method that calls another:
+
+```kotlin
+fun byteToHex(b: Int): String {
+ val result = CharArray(2)
+ result[0] = hexDigit((b ushr 4) and 0xf)
+ result[1] = hexDigit(b and 0xf)
+ return String(result)
+}
+
+fun hexDigit(i: Int): Char {
+ return (if (i < 10) i + '0'.toInt() else i - 10 + 'a'.toInt()).toChar()
+}
+```
+
+When generating the code above, we pass the `hexDigit()` method as an argument to the `byteToHex()`
+method using `%N`:
+
+```kotlin
+val hexDigit = FunSpec.builder("hexDigit")
+ .addParameter("i", Int::class)
+ .returns(Char::class)
+ .addStatement("return (if (i < 10) i + '0'.toInt() else i - 10 + 'a'.toInt()).toChar()")
+ .build()
+
+val byteToHex = FunSpec.builder("byteToHex")
+ .addParameter("b", Int::class)
+ .returns(String::class)
+ .addStatement("val result = CharArray(2)")
+ .addStatement("result[0] = %N((b ushr 4) and 0xf)", hexDigit)
+ .addStatement("result[1] = %N(b and 0xf)", hexDigit)
+ .addStatement("return String(result)")
+ .build()
+```
+
+Another handy feature that `%N` provides is automatically escaping names that contain illegal
+identifier characters with double ticks. Suppose your code creates a `MemberName` with a Kotlin
+keyword as the simple name:
+
+```kotlin
+val taco = ClassName("com.squareup.tacos", "Taco")
+val packager = ClassName("com.squareup.tacos", "TacoPackager")
+val file = FileSpec.builder("com.example", "Test")
+ .addFunction(
+ FunSpec.builder("packageTacos")
+ .addParameter("tacos", LIST.parameterizedBy(taco))
+ .addParameter("packager", packager)
+ .addStatement("packager.%N(tacos)", packager.member("package"))
+ .build()
+ )
+ .build()
+```
+
+`%N` will escape the name for you, ensuring that the output will pass compilation:
+
+```kotlin
+package com.example
+
+import com.squareup.tacos.Taco
+import com.squareup.tacos.TacoPackager
+import kotlin.collections.List
+
+fun packageTacos(tacos: List<Taco>, packager: TacoPackager) {
+ packager.`package`(tacos)
+}
+```
diff --git a/docs/objects.md b/docs/objects.md
new file mode 100644
index 0000000..75d9aab
--- /dev/null
+++ b/docs/objects.md
@@ -0,0 +1,42 @@
+Objects
+=======
+
+KotlinPoet supports objects:
+
+```kotlin
+val helloWorld = TypeSpec.objectBuilder("HelloWorld")
+ .addProperty(
+ PropertySpec.builder("buzz", String::class)
+ .initializer("%S", "buzz")
+ .build()
+ )
+ .addFunction(
+ FunSpec.builder("beep")
+ .addStatement("println(%S)", "Beep!")
+ .build()
+ )
+ .build()
+```
+
+Similarly, you can create companion objects and add them to classes using `addType()`:
+
+```kotlin
+val companion = TypeSpec.companionObjectBuilder()
+ .addProperty(
+ PropertySpec.builder("buzz", String::class)
+ .initializer("%S", "buzz")
+ .build()
+ )
+ .addFunction(
+ FunSpec.builder("beep")
+ .addStatement("println(%S)", "Beep!")
+ .build()
+ )
+ .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+ .addType(companion)
+ .build()
+```
+
+You can provide an optional name for a companion object.
diff --git a/docs/p-for-string-templates.md b/docs/p-for-string-templates.md
new file mode 100644
index 0000000..6b27e6a
--- /dev/null
+++ b/docs/p-for-string-templates.md
@@ -0,0 +1,67 @@
+%P for String Templates
+=======================
+
+`%S` also handles the escaping of dollar signs (`$`), to avoid inadvertent creation of string
+templates, which may fail to compile in generated code:
+
+```kotlin
+val stringWithADollar = "Your total is " + "$" + "50"
+val funSpec = FunSpec.builder("printTotal")
+ .returns(String::class)
+ .addStatement("return %S", stringWithADollar)
+ .build()
+```
+
+produces:
+
+```kotlin
+fun printTotal(): String = "Your total is ${'$'}50"
+```
+
+If you need to generate string templates, use `%P`, which doesn't escape dollars:
+
+```kotlin
+val amount = 50
+val stringWithADollar = "Your total is " + "$" + "amount"
+val funSpec = FunSpec.builder("printTotal")
+ .returns(String::class)
+ .addStatement("return %P", stringWithADollar)
+ .build()
+```
+
+produces:
+
+```kotlin
+fun printTotal(): String = "Your total is $amount"
+```
+
+You can also use `CodeBlock`s as arguments to `%P`, which is handy when you need to reference
+importable types or members inside the string template:
+
+```kotlin
+val file = FileSpec.builder("com.example", "Digits")
+ .addFunction(
+ FunSpec.builder("print")
+ .addParameter("digits", IntArray::class)
+ .addStatement("println(%P)", buildCodeBlock {
+ val contentToString = MemberName("kotlin.collections", "contentToString")
+ add("These are the digits: \${digits.%M()}", contentToString)
+ })
+ .build()
+ )
+ .build()
+println(file)
+```
+
+The snippet above will produce the following output, handling the imports properly:
+
+```kotlin
+package com.example
+
+import kotlin.IntArray
+import kotlin.collections.contentToString
+
+fun print(digits: IntArray) {
+ println("""These are the digits: ${digits.contentToString()}""")
+}
+```
diff --git a/docs/parameters.md b/docs/parameters.md
new file mode 100644
index 0000000..d816508
--- /dev/null
+++ b/docs/parameters.md
@@ -0,0 +1,25 @@
+Parameters
+==========
+
+Declare parameters on methods and constructors with either `ParameterSpec.builder()` or
+`FunSpec`'s convenient `addParameter()` API:
+
+```kotlin
+val android = ParameterSpec.builder("android", String::class)
+ .defaultValue("\"pie\"")
+ .build()
+
+val welcomeOverlords = FunSpec.builder("welcomeOverlords")
+ .addParameter(android)
+ .addParameter("robot", String::class)
+ .build()
+```
+
+The code above generates:
+
+```kotlin
+fun welcomeOverlords(android: String = "pie", robot: String) {
+}
+```
+
+The extended `Builder` form is necessary when the parameter has annotations (such as `@Inject`).
diff --git a/docs/properties.md b/docs/properties.md
new file mode 100644
index 0000000..cb878ab
--- /dev/null
+++ b/docs/properties.md
@@ -0,0 +1,142 @@
+Properties
+==========
+
+Like parameters, properties can be created either with builders or by using convenient helper
+methods:
+
+```kotlin
+val android = PropertySpec.builder("android", String::class)
+ .addModifiers(KModifier.PRIVATE)
+ .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+ .addProperty(android)
+ .addProperty("robot", String::class, KModifier.PRIVATE)
+ .build()
+```
+
+Which generates:
+
+```kotlin
+class HelloWorld {
+ private val android: String
+
+ private val robot: String
+}
+```
+
+The extended `Builder` form is necessary when a field has KDoc, annotations, or a field
+initializer. Field initializers use the same [`String.format()`][formatter]-like syntax as the code
+blocks above:
+
+```kotlin
+val android = PropertySpec.builder("android", String::class)
+ .addModifiers(KModifier.PRIVATE)
+ .initializer("%S + %L", "Oreo v.", 8.1)
+ .build()
+```
+
+Which generates:
+
+```kotlin
+private val android: String = "Oreo v." + 8.1
+```
+
+By default `PropertySpec.Builder` produces `val` properties. Use `mutable()` if you need a
+`var`:
+
+```kotlin
+val android = PropertySpec.builder("android", String::class)
+ .mutable()
+ .addModifiers(KModifier.PRIVATE)
+ .initializer("%S + %L", "Oreo v.", 8.1)
+ .build()
+```
+
+## Inline properties
+
+The way KotlinPoet models inline properties deserves special mention. The following snippet of code:
+
+```kotlin
+val android = PropertySpec.builder("android", String::class)
+ .mutable()
+ .addModifiers(KModifier.INLINE)
+ .build()
+```
+
+will produce an error:
+
+```
+java.lang.IllegalArgumentException: KotlinPoet doesn't allow setting the inline modifier on
+properties. You should mark either the getter, the setter, or both inline.
+```
+
+Indeed, a property marked with `inline` should have at least one accessor which will be inlined by
+the compiler. Let's add a getter to this property:
+
+```kotlin
+val android = PropertySpec.builder("android", String::class)
+ .mutable()
+ .getter(
+ FunSpec.getterBuilder()
+ .addModifiers(KModifier.INLINE)
+ .addStatement("return %S", "foo")
+ .build()
+ )
+ .build()
+```
+
+The result is the following:
+
+```kotlin
+var android: kotlin.String
+ inline get() = "foo"
+```
+
+Now, what if we wanted to add a non-inline setter to the property above? We can do so without
+modifying any of the code we wrote previously:
+
+```kotlin
+val android = PropertySpec.builder("android", String::class)
+ .mutable()
+ .getter(
+ FunSpec.getterBuilder()
+ .addModifiers(KModifier.INLINE)
+ .addStatement("return %S", "foo")
+ .build()
+ )
+ .setter(
+ FunSpec.setterBuilder()
+ .addParameter("value", String::class)
+ .build()
+ )
+ .build()
+```
+
+We get the expected result:
+
+```kotlin
+var android: kotlin.String
+ inline get() = "foo"
+ set(`value`) {
+ }
+```
+
+Finally, if we go back and add `KModifier.INLINE` to the setter, KotlinPoet can wrap it nicely and
+produce the following result:
+
+```kotlin
+inline var android: kotlin.String
+ get() = "foo"
+ set(`value`) {
+ }
+```
+
+Removing the modifier from either the getter or the setter will unwrap the expression back.
+
+If, on the other hand, KotlinPoet had allowed marking a property `inline` directly, the programmer
+would have had to manually add/remove the modifier whenever the state of the accessors changes in
+order to get correct and compilable output. We're solving this problem by making accessors the
+source of truth for the `inline` modifier.
+
+ [formatter]: https://developer.android.com/reference/java/util/Formatter.html
diff --git a/docs/s-for-strings.md b/docs/s-for-strings.md
new file mode 100644
index 0000000..d14d7e7
--- /dev/null
+++ b/docs/s-for-strings.md
@@ -0,0 +1,41 @@
+%S for Strings
+==============
+
+When emitting code that includes string literals, we can use **`%S`** to emit a **string**, complete
+with wrapping quotation marks and escaping. Here's a program that emits 3 methods, each of which
+returns its own name:
+
+```kotlin
+fun main(args: Array<String>) {
+ val helloWorld = TypeSpec.classBuilder("HelloWorld")
+ .addFunction(whatsMyNameYo("slimShady"))
+ .addFunction(whatsMyNameYo("eminem"))
+ .addFunction(whatsMyNameYo("marshallMathers"))
+ .build()
+
+ val kotlinFile = FileSpec.builder("com.example.helloworld", "HelloWorld")
+ .addType(helloWorld)
+ .build()
+
+ kotlinFile.writeTo(System.out)
+}
+
+private fun whatsMyNameYo(name: String): FunSpec {
+ return FunSpec.builder(name)
+ .returns(String::class)
+ .addStatement("return %S", name)
+ .build()
+}
+```
+
+In this case, using `%S` gives us quotation marks:
+
+```kotlin
+class HelloWorld {
+ fun slimShady(): String = "slimShady"
+
+ fun eminem(): String = "eminem"
+
+ fun marshallMathers(): String = "marshallMathers"
+}
+```
diff --git a/docs/t-for-types.md b/docs/t-for-types.md
new file mode 100644
index 0000000..d0bfa4a
--- /dev/null
+++ b/docs/t-for-types.md
@@ -0,0 +1,161 @@
+%T for Types
+============
+
+KotlinPoet has rich built-in support for types, including automatic generation of `import`
+statements. Just use **`%T`** to reference **types**:
+
+```kotlin
+val today = FunSpec.builder("today")
+ .returns(Date::class)
+ .addStatement("return %T()", Date::class)
+ .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+ .addFunction(today)
+ .build()
+
+val kotlinFile = FileSpec.builder("com.example.helloworld", "HelloWorld")
+ .addType(helloWorld)
+ .build()
+
+kotlinFile.writeTo(System.out)
+```
+
+That generates the following `.kt` file, complete with the necessary `import`:
+
+```kotlin
+package com.example.helloworld
+
+import java.util.Date
+
+class HelloWorld {
+ fun today(): Date = Date()
+}
+```
+
+We passed `Date::class` to reference a class that just-so-happens to be available when we're
+generating code. This doesn't need to be the case. Here's a similar example, but this one
+references a class that doesn't exist (yet):
+
+```kotlin
+val hoverboard = ClassName("com.mattel", "Hoverboard")
+
+val tomorrow = FunSpec.builder("tomorrow")
+ .returns(hoverboard)
+ .addStatement("return %T()", hoverboard)
+ .build()
+```
+
+And that not-yet-existent class is imported as well:
+
+```kotlin
+package com.example.helloworld
+
+import com.mattel.Hoverboard
+
+class HelloWorld {
+ fun tomorrow(): Hoverboard = Hoverboard()
+}
+```
+
+The `ClassName` type is very important, and you'll need it frequently when you're using KotlinPoet.
+It can identify any _declared_ class. Declared types are just the beginning of Kotlin's rich type
+system: we also have arrays, parameterized types, wildcard types, lambda types and type variables.
+KotlinPoet has classes for building each of these:
+
+```kotlin
+import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
+import com.squareup.kotlinpoet.STAR
+
+val hoverboard = ClassName("com.mattel", "Hoverboard")
+val list = ClassName("kotlin.collections", "List")
+val arrayList = ClassName("kotlin.collections", "ArrayList")
+val listOfHoverboards = list.parameterizedBy(hoverboard)
+val arrayListOfHoverboards = arrayList.parameterizedBy(hoverboard)
+
+val thing = ClassName("com.misc", "Thing")
+val array = ClassName("kotlin", "Array")
+val producerArrayOfThings = array.parameterizedBy(WildcardTypeName.producerOf(thing))
+
+val beyond = FunSpec.builder("beyond")
+ .returns(listOfHoverboards)
+ .addStatement("val result = %T()", arrayListOfHoverboards)
+ .addStatement("result += %T()", hoverboard)
+ .addStatement("result += %T()", hoverboard)
+ .addStatement("result += %T()", hoverboard)
+ .addStatement("return result")
+ .build()
+
+val printThings = FunSpec.builder("printThings")
+ .addParameter("things", producerArrayOfThings)
+ .addStatement("println(things)")
+ .build()
+
+val printKClass = FunSpec.builder("printKClass")
+ .addParameter("kClass", KClass::class.asClassName().parameterizedBy(STAR))
+ .addStatement("println(kClass)")
+ .build()
+```
+
+The `STAR` is represented as `*` in KotlinPoet. You can find more in the [KDoc][kdoc].
+
+KotlinPoet will decompose each type and import its components where possible.
+
+```kotlin
+package com.example.helloworld
+
+import com.mattel.Hoverboard
+import com.misc.Thing
+import kotlin.Array
+import kotlin.collections.ArrayList
+import kotlin.collections.List
+import kotlin.reflect.KClass
+
+class HelloWorld {
+ fun beyond(): List<Hoverboard> {
+ val result = ArrayList<Hoverboard>()
+ result += Hoverboard()
+ result += Hoverboard()
+ result += Hoverboard()
+ return result
+ }
+
+ fun printThings(things: Array<out Thing>) {
+ println(things)
+ }
+
+ fun printKClass(kClass: KClass<*>) {
+ println(kClass)
+ }
+}
+```
+
+## Nullable Types
+
+KotlinPoet supports nullable types. To convert a `TypeName` into its nullable counterpart, use the
+`copy()` method with `nullable` parameter set to `true`:
+
+```kotlin
+val java = PropertySpec.builder("java", String::class.asTypeName().copy(nullable = true))
+ .mutable()
+ .addModifiers(KModifier.PRIVATE)
+ .initializer("null")
+ .build()
+
+val helloWorld = TypeSpec.classBuilder("HelloWorld")
+ .addProperty(java)
+ .addProperty("kotlin", String::class, KModifier.PRIVATE)
+ .build()
+```
+
+generates:
+
+```kotlin
+class HelloWorld {
+ private var java: String? = null
+
+ private val kotlin: String
+}
+```
+
+ [kdoc]: https://square.github.io/kotlinpoet/1.x/kotlinpoet/kotlinpoet/com.squareup.kotlinpoet/
diff --git a/docs/type-aliases.md b/docs/type-aliases.md
new file mode 100644
index 0000000..c4aa2a7
--- /dev/null
+++ b/docs/type-aliases.md
@@ -0,0 +1,49 @@
+Type Aliases
+============
+
+KotlinPoet provides API for creating Type Aliases, which supports simple class names, parameterized
+types and lambdas:
+
+```kotlin
+val k = TypeVariableName("K")
+val t = TypeVariableName("T")
+
+val fileTable = Map::class.asClassName()
+ .parameterizedBy(k, Set::class.parameterizedBy(File::class))
+
+val predicate = LambdaTypeName.get(
+ parameters = arrayOf(t),
+ returnType = Boolean::class.asClassName()
+)
+val helloWorld = FileSpec.builder("com.example", "HelloWorld")
+ .addTypeAlias(TypeAliasSpec.builder("Word", String::class).build())
+ .addTypeAlias(
+ TypeAliasSpec.builder("FileTable", fileTable)
+ .addTypeVariable(k)
+ .build()
+ )
+ .addTypeAlias(
+ TypeAliasSpec.builder("Predicate", predicate)
+ .addTypeVariable(t)
+ .build()
+ )
+ .build()
+```
+
+Which generates the following:
+
+```kotlin
+package com.example
+
+import java.io.File
+import kotlin.Boolean
+import kotlin.String
+import kotlin.collections.Map
+import kotlin.collections.Set
+
+typealias Word = String
+
+typealias FileTable<K> = Map<K, Set<File>>
+
+typealias Predicate<T> = (T) -> Boolean
+```
diff --git a/gradle.properties b/gradle.properties
index dc6f725..5ee29f6 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,7 +1,7 @@
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
GROUP=com.squareup
-VERSION_NAME=1.15.0-SNAPSHOT
+VERSION_NAME=1.16.0
POM_URL=https://github.com/square/kotlinpoet
POM_SCM_URL=https://github.com/square/kotlinpoet
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 6bba5eb..1f39d95 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -13,22 +13,23 @@
# limitations under the License.
[versions]
-kotlin = "1.9.10"
-kct = "0.3.2"
-ksp = "1.9.10-1.0.13"
+kotlin = "1.9.22"
+kct = "0.4.0"
+ksp = "1.9.22-1.0.16"
ktlint = "0.48.2"
[plugins]
+kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
-dokka = { id = "org.jetbrains.dokka", version = "1.9.0" }
+dokka = { id = "org.jetbrains.dokka", version = "1.9.10" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
-spotless = { id = "com.diffplug.spotless", version = "6.22.0" }
-mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.25.3" }
+spotless = { id = "com.diffplug.spotless", version = "6.24.0" }
+mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.27.0" }
kotlinBinaryCompatibilityValidator = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version = "0.13.2" }
[libraries]
autoCommon = { module = "com.google.auto:auto-common", version = "1.2.2" }
-guava = { module = "com.google.guava:guava", version = "32.1.2-jre" }
+guava = { module = "com.google.guava:guava", version = "33.0.0-jre" }
javapoet = "com.squareup:javapoet:1.13.0"
autoService = "com.google.auto.service:auto-service-annotations:1.1.1"
@@ -38,12 +39,12 @@
kotlin-annotationProcessingEmbeddable = { module = "org.jetbrains.kotlin:kotlin-annotation-processing-embeddable", version.ref = "kotlin" }
kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
kotlin-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
-kotlin-metadata = { module = "org.jetbrains.kotlinx:kotlinx-metadata-jvm", version = "0.7.0" }
+kotlin-metadata = { module = "org.jetbrains.kotlinx:kotlinx-metadata-jvm", version = "0.8.0" }
ksp = { module = "com.google.devtools.ksp:symbol-processing", version.ref = "ksp" }
ksp-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" }
-truth = { module = "com.google.truth:truth", version = "1.1.5" }
+truth = { module = "com.google.truth:truth", version = "1.2.0" }
compileTesting = { module = "com.google.testing.compile:compile-testing", version = "0.21.0" }
jimfs = { module = "com.google.jimfs:jimfs", version = "1.3.0" }
ecj = { module = "org.eclipse.jdt.core.compiler:ecj", version = "4.6.1" }
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 7f93135..d64cd49 100644
--- a/gradle/wrapper/gradle-wrapper.jar
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 8838ba9..e6aba25 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/interop/javapoet/build.gradle.kts b/interop/javapoet/build.gradle.kts
index 734d934..8fd35cb 100644
--- a/interop/javapoet/build.gradle.kts
+++ b/interop/javapoet/build.gradle.kts
@@ -13,6 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+plugins {
+ kotlin("jvm")
+}
tasks.jar {
manifest {
diff --git a/interop/kotlinx-metadata/build.gradle.kts b/interop/kotlinx-metadata/build.gradle.kts
index a828789..6ac626e 100644
--- a/interop/kotlinx-metadata/build.gradle.kts
+++ b/interop/kotlinx-metadata/build.gradle.kts
@@ -13,6 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+plugins {
+ kotlin("jvm")
+}
tasks.jar {
manifest {
diff --git a/interop/ksp/build.gradle.kts b/interop/ksp/build.gradle.kts
index 3d615a3..782d151 100644
--- a/interop/ksp/build.gradle.kts
+++ b/interop/ksp/build.gradle.kts
@@ -13,6 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+plugins {
+ kotlin("jvm")
+}
tasks.jar {
manifest {
diff --git a/interop/ksp/src/main/kotlin/com/squareup/kotlinpoet/ksp/KsTypes.kt b/interop/ksp/src/main/kotlin/com/squareup/kotlinpoet/ksp/KsTypes.kt
index 8d07220..3c57aad 100644
--- a/interop/ksp/src/main/kotlin/com/squareup/kotlinpoet/ksp/KsTypes.kt
+++ b/interop/ksp/src/main/kotlin/com/squareup/kotlinpoet/ksp/KsTypes.kt
@@ -15,6 +15,7 @@
*/
package com.squareup.kotlinpoet.ksp
+import com.google.devtools.ksp.symbol.KSCallableReference
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSType
import com.google.devtools.ksp.symbol.KSTypeAlias
@@ -27,6 +28,8 @@
import com.google.devtools.ksp.symbol.Variance.INVARIANT
import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.KModifier
+import com.squareup.kotlinpoet.LambdaTypeName
+import com.squareup.kotlinpoet.ParameterSpec
import com.squareup.kotlinpoet.STAR
import com.squareup.kotlinpoet.TypeName
import com.squareup.kotlinpoet.TypeVariableName
@@ -179,5 +182,15 @@
public fun KSTypeReference.toTypeName(
typeParamResolver: TypeParameterResolver = TypeParameterResolver.EMPTY,
): TypeName {
- return resolve().toTypeName(typeParamResolver, element?.typeArguments.orEmpty())
+ val type = resolve()
+ return when (val elem = element) {
+ is KSCallableReference -> {
+ LambdaTypeName.get(
+ receiver = elem.receiverType?.toTypeName(typeParamResolver),
+ parameters = elem.functionParameters.map { ParameterSpec.unnamed(it.type.toTypeName(typeParamResolver)) },
+ returnType = elem.returnType.toTypeName(typeParamResolver),
+ ).copy(nullable = type.isMarkedNullable, suspending = type.isSuspendFunctionType)
+ }
+ else -> type.toTypeName(typeParamResolver, element?.typeArguments.orEmpty())
+ }
}
diff --git a/interop/ksp/test-processor/build.gradle.kts b/interop/ksp/test-processor/build.gradle.kts
index f0a4d71..d2e3fb7 100644
--- a/interop/ksp/test-processor/build.gradle.kts
+++ b/interop/ksp/test-processor/build.gradle.kts
@@ -15,6 +15,7 @@
*/
plugins {
id("com.google.devtools.ksp")
+ kotlin("jvm")
}
tasks.compileTestKotlin {
diff --git a/interop/ksp/test-processor/src/main/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessor.kt b/interop/ksp/test-processor/src/main/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessor.kt
index 82064c8..fb0a5f0 100644
--- a/interop/ksp/test-processor/src/main/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessor.kt
+++ b/interop/ksp/test-processor/src/main/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessor.kt
@@ -171,7 +171,14 @@
)
.addParameters(
function.parameters.map { parameter ->
- val parameterType = parameter.type.toValidatedTypeName(functionTypeParams).let {
+ // Function references can't be obtained from a resolved KSType because it resolves to FunctionN<> which
+ // loses the necessary context, skip validation in these cases as we know they won't match.
+ val typeName = if (parameter.type.resolve().run { isFunctionType || isSuspendFunctionType }) {
+ parameter.type.toTypeName(functionTypeParams)
+ } else {
+ parameter.type.toValidatedTypeName(functionTypeParams)
+ }
+ val parameterType = typeName.let {
if (unwrapTypeAliases) {
it.unwrapTypeAlias()
} else {
diff --git a/interop/ksp/test-processor/src/test/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessorTest.kt b/interop/ksp/test-processor/src/test/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessorTest.kt
index 1aaa1ed..06bf588 100644
--- a/interop/ksp/test-processor/src/test/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessorTest.kt
+++ b/interop/ksp/test-processor/src/test/kotlin/com/squareup/kotlinpoet/ksp/test/processor/TestProcessorTest.kt
@@ -129,7 +129,12 @@
suspend fun functionD(
param1: () -> String,
param2: (String) -> String,
- param3: String.() -> String
+ param3: String.() -> String,
+ param4: Function0<String>,
+ param5: Function1<String, String>,
+ param6: (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) -> Unit,
+ param7: ((String) -> String)?,
+ param8: suspend () -> String,
) {
}
@@ -188,6 +193,7 @@
import kotlin.Int
import kotlin.IntArray
import kotlin.String
+ import kotlin.Unit
import kotlin.collections.List
import kotlin.collections.Map
import kotlin.collections.MutableList
@@ -261,9 +267,39 @@
}
public suspend fun functionD(
- param1: Function0<String>,
- param2: Function1<String, String>,
- param3: Function1<String, String>,
+ param1: () -> String,
+ param2: (String) -> String,
+ param3: String.() -> String,
+ param4: Function0<String>,
+ param5: Function1<String, String>,
+ param6: (
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ Int,
+ ) -> Unit,
+ param7: ((String) -> String)?,
+ param8: suspend () -> String,
) {
}
diff --git a/kotlinpoet/api/kotlinpoet.api b/kotlinpoet/api/kotlinpoet.api
index 114bd19..4e92873 100644
--- a/kotlinpoet/api/kotlinpoet.api
+++ b/kotlinpoet/api/kotlinpoet.api
@@ -181,7 +181,7 @@
public abstract interface annotation class com/squareup/kotlinpoet/ExperimentalKotlinPoetApi : java/lang/annotation/Annotation {
}
-public final class com/squareup/kotlinpoet/FileSpec : com/squareup/kotlinpoet/Annotatable, com/squareup/kotlinpoet/Taggable {
+public final class com/squareup/kotlinpoet/FileSpec : com/squareup/kotlinpoet/Annotatable, com/squareup/kotlinpoet/Taggable, com/squareup/kotlinpoet/TypeSpecHolder {
public static final field Companion Lcom/squareup/kotlinpoet/FileSpec$Companion;
public static final fun builder (Lcom/squareup/kotlinpoet/ClassName;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
public static final fun builder (Lcom/squareup/kotlinpoet/MemberName;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
@@ -195,7 +195,9 @@
public final fun getMembers ()Ljava/util/List;
public final fun getName ()Ljava/lang/String;
public final fun getPackageName ()Ljava/lang/String;
+ public final fun getRelativePath ()Ljava/lang/String;
public fun getTags ()Ljava/util/Map;
+ public fun getTypeSpecs ()Ljava/util/List;
public fun hashCode ()I
public final fun isScript ()Z
public static final fun scriptBuilder (Ljava/lang/String;Ljava/lang/String;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
@@ -207,13 +209,15 @@
public static synthetic fun toBuilder$default (Lcom/squareup/kotlinpoet/FileSpec;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
public final fun toJavaFileObject ()Ljavax/tools/JavaFileObject;
public fun toString ()Ljava/lang/String;
- public final fun writeTo (Ljava/io/File;)V
+ public final fun writeTo (Ljava/io/File;)Ljava/io/File;
+ public final synthetic fun writeTo (Ljava/io/File;)V
public final fun writeTo (Ljava/lang/Appendable;)V
- public final fun writeTo (Ljava/nio/file/Path;)V
+ public final fun writeTo (Ljava/nio/file/Path;)Ljava/nio/file/Path;
+ public final synthetic fun writeTo (Ljava/nio/file/Path;)V
public final fun writeTo (Ljavax/annotation/processing/Filer;)V
}
-public final class com/squareup/kotlinpoet/FileSpec$Builder : com/squareup/kotlinpoet/Annotatable$Builder, com/squareup/kotlinpoet/Taggable$Builder {
+public final class com/squareup/kotlinpoet/FileSpec$Builder : com/squareup/kotlinpoet/Annotatable$Builder, com/squareup/kotlinpoet/Taggable$Builder, com/squareup/kotlinpoet/TypeSpecHolder$Builder {
public final fun addAliasedImport (Lcom/squareup/kotlinpoet/ClassName;Ljava/lang/String;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
public final fun addAliasedImport (Lcom/squareup/kotlinpoet/ClassName;Ljava/lang/String;Ljava/lang/String;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
public final fun addAliasedImport (Lcom/squareup/kotlinpoet/MemberName;Ljava/lang/String;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
@@ -251,8 +255,11 @@
public final fun addNamedCode (Ljava/lang/String;Ljava/util/Map;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
public final fun addProperty (Lcom/squareup/kotlinpoet/PropertySpec;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
public final fun addStatement (Ljava/lang/String;[Ljava/lang/Object;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
- public final fun addType (Lcom/squareup/kotlinpoet/TypeSpec;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
+ public fun addType (Lcom/squareup/kotlinpoet/TypeSpec;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
+ public synthetic fun addType (Lcom/squareup/kotlinpoet/TypeSpec;)Lcom/squareup/kotlinpoet/TypeSpecHolder$Builder;
public final fun addTypeAlias (Lcom/squareup/kotlinpoet/TypeAliasSpec;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
+ public fun addTypes (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
+ public synthetic fun addTypes (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/TypeSpecHolder$Builder;
public final fun beginControlFlow (Ljava/lang/String;[Ljava/lang/Object;)Lcom/squareup/kotlinpoet/FileSpec$Builder;
public final fun build ()Lcom/squareup/kotlinpoet/FileSpec;
public final fun clearBody ()Lcom/squareup/kotlinpoet/FileSpec$Builder;
@@ -554,6 +561,7 @@
public final class com/squareup/kotlinpoet/NameAllocator {
public fun <init> ()V
+ public fun <init> (Z)V
public final fun copy ()Lcom/squareup/kotlinpoet/NameAllocator;
public final fun get (Ljava/lang/Object;)Ljava/lang/String;
public final fun newName (Ljava/lang/String;)Ljava/lang/String;
@@ -925,7 +933,7 @@
public static final fun get (Lkotlin/reflect/KClass;)Lcom/squareup/kotlinpoet/ClassName;
}
-public final class com/squareup/kotlinpoet/TypeSpec : com/squareup/kotlinpoet/Annotatable, com/squareup/kotlinpoet/ContextReceivable, com/squareup/kotlinpoet/Documentable, com/squareup/kotlinpoet/OriginatingElementsHolder, com/squareup/kotlinpoet/Taggable {
+public final class com/squareup/kotlinpoet/TypeSpec : com/squareup/kotlinpoet/Annotatable, com/squareup/kotlinpoet/ContextReceivable, com/squareup/kotlinpoet/Documentable, com/squareup/kotlinpoet/OriginatingElementsHolder, com/squareup/kotlinpoet/Taggable, com/squareup/kotlinpoet/TypeSpecHolder {
public static final field Companion Lcom/squareup/kotlinpoet/TypeSpec$Companion;
public static final fun annotationBuilder (Lcom/squareup/kotlinpoet/ClassName;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
public static final fun annotationBuilder (Ljava/lang/String;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
@@ -959,7 +967,7 @@
public final fun getSuperclassConstructorParameters ()Ljava/util/List;
public final fun getSuperinterfaces ()Ljava/util/Map;
public fun getTags ()Ljava/util/Map;
- public final fun getTypeSpecs ()Ljava/util/List;
+ public fun getTypeSpecs ()Ljava/util/List;
public final fun getTypeVariables ()Ljava/util/List;
public fun hashCode ()I
public static final fun interfaceBuilder (Lcom/squareup/kotlinpoet/ClassName;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
@@ -981,7 +989,7 @@
public static final fun valueClassBuilder (Ljava/lang/String;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
}
-public final class com/squareup/kotlinpoet/TypeSpec$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 {
+public final class com/squareup/kotlinpoet/TypeSpec$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, com/squareup/kotlinpoet/TypeSpecHolder$Builder {
public synthetic fun addAnnotation (Lcom/squareup/kotlinpoet/AnnotationSpec;)Lcom/squareup/kotlinpoet/Annotatable$Builder;
public fun addAnnotation (Lcom/squareup/kotlinpoet/AnnotationSpec;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
public synthetic fun addAnnotation (Lcom/squareup/kotlinpoet/ClassName;)Lcom/squareup/kotlinpoet/Annotatable$Builder;
@@ -1023,10 +1031,12 @@
public static synthetic fun addSuperinterface$default (Lcom/squareup/kotlinpoet/TypeSpec$Builder;Ljava/lang/reflect/Type;Lcom/squareup/kotlinpoet/CodeBlock;ILjava/lang/Object;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
public static synthetic fun addSuperinterface$default (Lcom/squareup/kotlinpoet/TypeSpec$Builder;Lkotlin/reflect/KClass;Lcom/squareup/kotlinpoet/CodeBlock;ILjava/lang/Object;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
public final fun addSuperinterfaces (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
- public final fun addType (Lcom/squareup/kotlinpoet/TypeSpec;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
+ public fun addType (Lcom/squareup/kotlinpoet/TypeSpec;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
+ public synthetic fun addType (Lcom/squareup/kotlinpoet/TypeSpec;)Lcom/squareup/kotlinpoet/TypeSpecHolder$Builder;
public final fun addTypeVariable (Lcom/squareup/kotlinpoet/TypeVariableName;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
public final fun addTypeVariables (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
- public final fun addTypes (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
+ public fun addTypes (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/TypeSpec$Builder;
+ public synthetic fun addTypes (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/TypeSpecHolder$Builder;
public final fun build ()Lcom/squareup/kotlinpoet/TypeSpec;
public synthetic fun contextReceivers (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/ContextReceivable$Builder;
public final fun getAnnotationSpecs ()Ljava/util/List;
@@ -1081,6 +1091,15 @@
public static fun values ()[Lcom/squareup/kotlinpoet/TypeSpec$Kind;
}
+public abstract interface class com/squareup/kotlinpoet/TypeSpecHolder {
+ public abstract fun getTypeSpecs ()Ljava/util/List;
+}
+
+public abstract interface class com/squareup/kotlinpoet/TypeSpecHolder$Builder {
+ public abstract fun addType (Lcom/squareup/kotlinpoet/TypeSpec;)Lcom/squareup/kotlinpoet/TypeSpecHolder$Builder;
+ public fun addTypes (Ljava/lang/Iterable;)Lcom/squareup/kotlinpoet/TypeSpecHolder$Builder;
+}
+
public final class com/squareup/kotlinpoet/TypeVariableName : com/squareup/kotlinpoet/TypeName {
public static final field Companion Lcom/squareup/kotlinpoet/TypeVariableName$Companion;
public final fun copy (ZLjava/util/List;Ljava/util/List;ZLjava/util/Map;)Lcom/squareup/kotlinpoet/TypeVariableName;
diff --git a/kotlinpoet/build.gradle.kts b/kotlinpoet/build.gradle.kts
index 9627b60..48669b3 100644
--- a/kotlinpoet/build.gradle.kts
+++ b/kotlinpoet/build.gradle.kts
@@ -13,40 +13,59 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-tasks.jar {
- manifest {
- attributes("Automatic-Module-Name" to "com.squareup.kotlinpoet")
- }
-}
-
-tasks.compileTestKotlin {
- compilerOptions {
- freeCompilerArgs.add("-opt-in=com.squareup.kotlinpoet.DelicateKotlinPoetApi")
- }
+plugins {
+ kotlin("multiplatform")
}
spotless {
kotlin {
targetExclude(
// Non-Square licensed files
- "src/main/java/com/squareup/kotlinpoet/ClassName.kt",
- "src/test/java/com/squareup/kotlinpoet/AbstractTypesTest.kt",
- "src/test/java/com/squareup/kotlinpoet/ClassNameTest.kt",
- "src/test/java/com/squareup/kotlinpoet/TypesEclipseTest.kt",
- "src/test/java/com/squareup/kotlinpoet/TypesTest.kt",
+ "src/commonMain/kotlin/com/squareup/kotlinpoet/ClassName.kt",
+ "src/commonTest/kotlin/com/squareup/kotlinpoet/AbstractTypesTest.kt",
+ "src/commonTest/kotlin/com/squareup/kotlinpoet/ClassNameTest.kt",
+ "src/commonTest/kotlin/com/squareup/kotlinpoet/TypesEclipseTest.kt",
+ "src/commonTest/kotlin/com/squareup/kotlinpoet/TypesTest.kt",
)
}
}
-dependencies {
- implementation(libs.kotlin.reflect)
- testImplementation(libs.kotlin.junit)
- testImplementation(libs.truth)
- testImplementation(libs.guava)
- testImplementation(libs.compileTesting)
- testImplementation(libs.jimfs)
- testImplementation(libs.ecj)
- testImplementation(libs.kotlinCompileTesting)
- testImplementation(libs.kotlin.annotationProcessingEmbeddable)
- testImplementation(libs.kotlin.compilerEmbeddable)
+kotlin {
+ jvm {
+ withJava()
+ }
+
+ sourceSets {
+ val commonMain by getting {
+ dependencies {
+ implementation(libs.kotlin.reflect)
+ }
+ }
+ val commonTest by getting {
+ dependencies {
+ implementation(libs.kotlin.junit)
+ implementation(libs.truth)
+ implementation(libs.guava)
+ implementation(libs.compileTesting)
+ implementation(libs.jimfs)
+ implementation(libs.ecj)
+ implementation(libs.kotlinCompileTesting)
+ implementation(libs.kotlin.annotationProcessingEmbeddable)
+ implementation(libs.kotlin.compilerEmbeddable)
+ }
+ }
+ }
}
+
+tasks.withType(org.gradle.jvm.tasks.Jar::class.java) {
+ manifest {
+ attributes("Automatic-Module-Name" to "com.squareup.kotlinpoet")
+ }
+}
+
+tasks.named<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>("compileTestKotlinJvm") {
+ compilerOptions {
+ freeCompilerArgs.add("-opt-in=com.squareup.kotlinpoet.DelicateKotlinPoetApi")
+ }
+}
+
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Annotatable.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Annotatable.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/Annotatable.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Annotatable.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/AnnotationSpec.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/AnnotationSpec.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/AnnotationSpec.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/AnnotationSpec.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ClassName.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ClassName.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/ClassName.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ClassName.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/CodeBlock.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeBlock.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/CodeBlock.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeBlock.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/CodeWriter.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt
similarity index 98%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/CodeWriter.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt
index ff1de01..d4489f2 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/CodeWriter.kt
+++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/CodeWriter.kt
@@ -445,8 +445,8 @@
return className.canonicalName
}
- // If the class is in the same package, we're done.
- if (packageName == className.packageName) {
+ // If the class is in the same package and there's no import alias for that class, we're done.
+ if (packageName == className.packageName && imports[className.canonicalName]?.alias == null) {
referencedNames.add(className.topLevelClassName().simpleName)
return className.simpleNames.joinToString(".")
}
@@ -722,11 +722,11 @@
importsCollector.close()
return CodeWriter(
- out,
- indent,
- memberImports + generatedImports.filterKeys { it !in memberImports },
- suggestedTypeImports,
- suggestedMemberImports,
+ out = out,
+ indent = indent,
+ imports = memberImports + generatedImports.filterKeys { it !in memberImports },
+ importedTypes = suggestedTypeImports,
+ importedMembers = suggestedMemberImports,
)
}
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ContextReceivable.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ContextReceivable.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/ContextReceivable.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ContextReceivable.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/DelicateKotlinPoetApi.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/DelicateKotlinPoetApi.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/DelicateKotlinPoetApi.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/DelicateKotlinPoetApi.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Documentable.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Documentable.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/Documentable.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Documentable.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Dynamic.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Dynamic.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/Dynamic.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Dynamic.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ExperimentalKotlinPoetApi.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ExperimentalKotlinPoetApi.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/ExperimentalKotlinPoetApi.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ExperimentalKotlinPoetApi.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/FileSpec.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/FileSpec.kt
similarity index 89%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/FileSpec.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/FileSpec.kt
index ef3f032..288848c 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/FileSpec.kt
+++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/FileSpec.kt
@@ -20,16 +20,19 @@
import java.io.File
import java.io.IOException
import java.io.InputStream
-import java.io.OutputStreamWriter
import java.net.URI
import java.nio.charset.StandardCharsets.UTF_8
-import java.nio.file.Files
import java.nio.file.Path
import javax.annotation.processing.Filer
import javax.tools.JavaFileObject
import javax.tools.JavaFileObject.Kind
import javax.tools.SimpleJavaFileObject
import javax.tools.StandardLocation
+import kotlin.DeprecationLevel.HIDDEN
+import kotlin.io.path.createDirectories
+import kotlin.io.path.isDirectory
+import kotlin.io.path.notExists
+import kotlin.io.path.outputStream
import kotlin.reflect.KClass
/**
@@ -46,8 +49,9 @@
public class FileSpec private constructor(
builder: Builder,
private val tagMap: TagMap = builder.buildTagMap(),
-) : Taggable by tagMap, Annotatable {
+) : Taggable by tagMap, Annotatable, TypeSpecHolder {
override val annotations: List<AnnotationSpec> = builder.annotations.toImmutableList()
+ override val typeSpecs: List<TypeSpec> = builder.members.filterIsInstance<TypeSpec>().toImmutableList()
public val comment: CodeBlock = builder.comment.build()
public val packageName: String = builder.packageName
public val name: String = builder.name
@@ -59,6 +63,20 @@
private val indent = builder.indent
private val extension = if (isScript) "kts" else "kt"
+ /**
+ * The relative path of the file which would be produced by a call to [writeTo].
+ * This value always uses unix-style path separators (`/`).
+ */
+ public val relativePath: String = buildString {
+ for (packageComponent in packageName.split('.').dropLastWhile { it.isEmpty() }) {
+ append(packageComponent)
+ append('/')
+ }
+ append(name)
+ append('.')
+ append(extension)
+ }
+
@Throws(IOException::class)
public fun writeTo(out: Appendable) {
val codeWriter = CodeWriter.withCollectedImports(
@@ -71,28 +89,39 @@
codeWriter.close()
}
- /** Writes this to `directory` as UTF-8 using the standard directory structure. */
- @Throws(IOException::class)
- public fun writeTo(directory: Path) {
- require(Files.notExists(directory) || Files.isDirectory(directory)) {
- "path $directory exists but is not a directory."
- }
- var outputDirectory = directory
- if (packageName.isNotEmpty()) {
- for (packageComponent in packageName.split('.').dropLastWhile { it.isEmpty() }) {
- outputDirectory = outputDirectory.resolve(packageComponent)
- }
- }
-
- Files.createDirectories(outputDirectory)
-
- val outputPath = outputDirectory.resolve("$name.$extension")
- OutputStreamWriter(Files.newOutputStream(outputPath), UTF_8).use { writer -> writeTo(writer) }
+ @Deprecated("", level = HIDDEN)
+ @JvmName("writeTo") // For binary compatibility.
+ public fun oldWriteTo(directory: Path) {
+ writeTo(directory)
}
- /** Writes this to `directory` as UTF-8 using the standard directory structure. */
+ /**
+ * Writes this to [directory] as UTF-8 using the standard directory structure
+ * and returns the newly output path.
+ */
@Throws(IOException::class)
- public fun writeTo(directory: File): Unit = writeTo(directory.toPath())
+ public fun writeTo(directory: Path): Path {
+ require(directory.notExists() || directory.isDirectory()) {
+ "path $directory exists but is not a directory."
+ }
+ val outputPath = directory.resolve(relativePath)
+ outputPath.parent.createDirectories()
+ outputPath.outputStream().bufferedWriter().use(::writeTo)
+ return outputPath
+ }
+
+ @Deprecated("", level = HIDDEN)
+ @JvmName("writeTo") // For binary compatibility.
+ public fun oldWriteTo(directory: File) {
+ writeTo(directory)
+ }
+
+ /**
+ * Writes this to [directory] as UTF-8 using the standard directory structure
+ * and returns the newly output file.
+ */
+ @Throws(IOException::class)
+ public fun writeTo(directory: File): File = writeTo(directory.toPath()).toFile()
/** Writes this to `filer`. */
@Throws(IOException::class)
@@ -191,13 +220,7 @@
override fun toString(): String = buildString { writeTo(this) }
public fun toJavaFileObject(): JavaFileObject {
- val uri = URI.create(
- if (packageName.isEmpty()) {
- name
- } else {
- packageName.replace('.', '/') + '/' + name
- } + ".$extension",
- )
+ val uri = URI.create(relativePath)
return object : SimpleJavaFileObject(uri, Kind.SOURCE) {
private val lastModified = System.currentTimeMillis()
override fun getCharContent(ignoreEncodingErrors: Boolean): String {
@@ -230,7 +253,7 @@
public val packageName: String,
public val name: String,
public val isScript: Boolean,
- ) : Taggable.Builder<Builder>, Annotatable.Builder<Builder> {
+ ) : Taggable.Builder<Builder>, Annotatable.Builder<Builder>, TypeSpecHolder.Builder<Builder> {
override val annotations: MutableList<AnnotationSpec> = mutableListOf()
internal val comment = CodeBlock.builder()
internal val memberImports = sortedSetOf<Import>()
@@ -275,7 +298,7 @@
comment.clear()
}
- public fun addType(typeSpec: TypeSpec): Builder = apply {
+ override fun addType(typeSpec: TypeSpec): Builder = apply {
if (isScript) {
body.add("%L", typeSpec)
} else {
@@ -283,6 +306,11 @@
}
}
+ //region Overrides for binary compatibility
+ @Suppress("RedundantOverride")
+ override fun addTypes(typeSpecs: Iterable<TypeSpec>): Builder = super.addTypes(typeSpecs)
+ //endregion
+
public fun addFunction(funSpec: FunSpec): Builder = apply {
require(!funSpec.isConstructor && !funSpec.isAccessor) {
"cannot add ${funSpec.name} to file $name"
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/FunSpec.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/FunSpec.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/FunSpec.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/FunSpec.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Import.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Import.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/Import.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Import.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/KModifier.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/KModifier.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/KModifier.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/KModifier.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/KOperator.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/KOperator.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/KOperator.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/KOperator.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/LambdaTypeName.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/LambdaTypeName.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/LambdaTypeName.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/LambdaTypeName.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/LineWrapper.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/LineWrapper.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/LineWrapper.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/LineWrapper.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/MemberName.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/MemberName.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/MemberName.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/MemberName.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/NameAllocator.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/NameAllocator.kt
similarity index 77%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/NameAllocator.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/NameAllocator.kt
index cda1004..a6c570b 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/NameAllocator.kt
+++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/NameAllocator.kt
@@ -77,7 +77,39 @@
private val allocatedNames: MutableSet<String>,
private val tagToName: MutableMap<Any, String>,
) {
- public constructor() : this(mutableSetOf(), mutableMapOf())
+ public constructor() : this(preallocateKeywords = true)
+
+ /**
+ * @param preallocateKeywords If true, all Kotlin keywords will be preallocated. Requested names which
+ * collide with keywords will be suffixed with underscores to avoid being used as identifiers:
+ *
+ * ```kotlin
+ * val nameAllocator = NameAllocator(preallocateKeywords = true)
+ * println(nameAllocator.newName("when")) // prints "when_"
+ * ```
+ *
+ * If false, keywords will not get any special treatment:
+ *
+ * ```kotlin
+ * val nameAllocator = NameAllocator(preallocateKeywords = false)
+ * println(nameAllocator.newName("when")) // prints "when"
+ * ```
+ *
+ * Note that you can use the `%N` placeholder when emitting a name produced by [NameAllocator] to
+ * ensure it's properly escaped for use as an identifier:
+ *
+ * ```kotlin
+ * val nameAllocator = NameAllocator(preallocateKeywords = false)
+ * println(CodeBlock.of("%N", nameAllocator.newName("when"))) // prints "`when`"
+ * ```
+ *
+ * The default behaviour of [NameAllocator] is to preallocate keywords - this is the behaviour you'll
+ * get when using the no-arg constructor.
+ */
+ public constructor(preallocateKeywords: Boolean) : this(
+ allocatedNames = if (preallocateKeywords) KEYWORDS.toMutableSet() else mutableSetOf(),
+ tagToName = mutableMapOf(),
+ )
/**
* Return a new name using `suggestion` that will not be a Java identifier or clash with other
@@ -89,7 +121,7 @@
tag: Any = UUID.randomUUID().toString(),
): String {
var result = toJavaIdentifier(suggestion)
- while (result.isKeyword || !allocatedNames.add(result)) {
+ while (!allocatedNames.add(result)) {
result += "_"
}
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/OriginatingElementsHolder.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/OriginatingElementsHolder.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/OriginatingElementsHolder.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/OriginatingElementsHolder.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ParameterSpec.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ParameterSpec.kt
similarity index 98%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/ParameterSpec.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ParameterSpec.kt
index 15cf4bf..91dc8b0 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ParameterSpec.kt
+++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ParameterSpec.kt
@@ -54,10 +54,8 @@
internal fun emit(
codeWriter: CodeWriter,
includeType: Boolean = true,
- emitKdoc: Boolean = false,
inlineAnnotations: Boolean = true,
) {
- if (emitKdoc) codeWriter.emitKdoc(kdoc.ensureEndsWithNewLine())
codeWriter.emitAnnotations(annotations, inlineAnnotations)
codeWriter.emitModifiers(modifiers)
if (name.isNotEmpty()) codeWriter.emitCode("%N", this)
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/ParameterizedTypeName.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ParameterizedTypeName.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/ParameterizedTypeName.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/ParameterizedTypeName.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/PropertySpec.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/PropertySpec.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/PropertySpec.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/PropertySpec.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Taggable.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Taggable.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/Taggable.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Taggable.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeAliasSpec.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeAliasSpec.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeAliasSpec.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeAliasSpec.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeName.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeName.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeName.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeName.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeSpec.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeSpec.kt
similarity index 95%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeSpec.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeSpec.kt
index 5f52e4f..af0613b 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeSpec.kt
+++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeSpec.kt
@@ -47,7 +47,8 @@
OriginatingElementsHolder by delegateOriginatingElements,
ContextReceivable by contextReceivers,
Annotatable,
- Documentable {
+ Documentable,
+ TypeSpecHolder {
public val kind: Kind = builder.kind
public val name: String? = builder.name
override val kdoc: CodeBlock = builder.kdoc.build()
@@ -76,7 +77,7 @@
public val initializerBlock: CodeBlock = builder.initializerBlock.build()
public val initializerIndex: Int = builder.initializerIndex
public val funSpecs: List<FunSpec> = builder.funSpecs.toImmutableList()
- public val typeSpecs: List<TypeSpec> = builder.typeSpecs.toImmutableList()
+ public override val typeSpecs: List<TypeSpec> = builder.typeSpecs.toImmutableList()
internal val nestedTypesSimpleNames = typeSpecs.map { it.name }.toImmutableSet()
@Deprecated("Use annotations property", ReplaceWith("annotations"), ERROR)
@@ -208,7 +209,7 @@
)
param.emitDefaultValue(codeWriter)
} else {
- param.emit(codeWriter, emitKdoc = true, inlineAnnotations = false)
+ param.emit(codeWriter, inlineAnnotations = false)
}
}
@@ -373,31 +374,25 @@
/**
* Returns KDoc comments including those of the primary constructor and its parameters.
*
- * If the primary constructor parameter is not mapped to a property, or if the property doesn't
- * have its own KDoc - the parameter's KDoc will be attached to the parameter. Otherwise, if both
- * the parameter and the property have KDoc - the property's KDoc will be attached to the
- * property/parameter, and the parameter's KDoc will be printed in the type header.
+ * Parameters' KDocs, if present, will always be printed in the type header.
*/
private fun kdocWithConstructorDocs(): CodeBlock {
- if (primaryConstructor == null) {
- return kdoc.ensureEndsWithNewLine()
- }
- val constructorProperties = constructorProperties()
- val parametersWithKdoc = primaryConstructor.parameters.filter { parameter ->
- val propertyKdoc = constructorProperties[parameter.name]?.kdoc ?: CodeBlock.EMPTY
- return@filter parameter.kdoc.isNotEmpty() && propertyKdoc.isNotEmpty() &&
- parameter.kdoc != propertyKdoc
- }
- return with(kdoc.ensureEndsWithNewLine().toBuilder()) {
- if (kdoc.isNotEmpty()) add("\n")
- if (primaryConstructor.kdoc.isNotEmpty()) {
- add("@constructor %L", primaryConstructor.kdoc.ensureEndsWithNewLine())
+ val classKdoc = kdoc.ensureEndsWithNewLine()
+ val constructorKdoc = buildCodeBlock {
+ if (primaryConstructor != null) {
+ if (primaryConstructor.kdoc.isNotEmpty()) {
+ add("@constructor %L", primaryConstructor.kdoc.ensureEndsWithNewLine())
+ }
+ primaryConstructor.parameters.forEach { parameter ->
+ if (parameter.kdoc.isNotEmpty()) {
+ add("@param %L %L", parameter.name, parameter.kdoc.ensureEndsWithNewLine())
+ }
+ }
}
- parametersWithKdoc.forEach { parameter ->
- add("@param %L %L", parameter.name, parameter.kdoc.ensureEndsWithNewLine())
- }
- build()
}
+ return listOf(classKdoc, constructorKdoc)
+ .filter(CodeBlock::isNotEmpty)
+ .joinToCode(separator = "\n")
}
private val hasInitializer: Boolean get() = initializerIndex != -1 && initializerBlock.isNotEmpty()
@@ -475,7 +470,8 @@
OriginatingElementsHolder.Builder<Builder>,
ContextReceivable.Builder<Builder>,
Annotatable.Builder<Builder>,
- Documentable.Builder<Builder> {
+ Documentable.Builder<Builder>,
+ TypeSpecHolder.Builder<Builder> {
internal var primaryConstructor: FunSpec? = null
internal var superclass: TypeName = ANY
internal val initializerBlock = CodeBlock.builder()
@@ -725,11 +721,7 @@
funSpecs += funSpec
}
- public fun addTypes(typeSpecs: Iterable<TypeSpec>): Builder = apply {
- this.typeSpecs += typeSpecs
- }
-
- public fun addType(typeSpec: TypeSpec): Builder = apply {
+ override fun addType(typeSpec: TypeSpec): Builder = apply {
typeSpecs += typeSpec
}
@@ -764,6 +756,9 @@
@Suppress("RedundantOverride")
override fun addKdoc(block: CodeBlock): Builder = super.addKdoc(block)
+
+ @Suppress("RedundantOverride")
+ override fun addTypes(typeSpecs: Iterable<TypeSpec>): Builder = super.addTypes(typeSpecs)
//endregion
public fun build(): TypeSpec {
diff --git a/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeSpecHolder.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeSpecHolder.kt
new file mode 100644
index 0000000..aad67e6
--- /dev/null
+++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeSpecHolder.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.squareup.kotlinpoet
+
+/** A spec which can contain other [TypeSpec] */
+public interface TypeSpecHolder {
+ public val typeSpecs: List<TypeSpec>
+
+ public interface Builder<out T : Builder<T>> {
+
+ public fun addType(typeSpec: TypeSpec): T
+
+ @Suppress("UNCHECKED_CAST")
+ public fun addTypes(typeSpecs: Iterable<TypeSpec>): T = apply {
+ for (typeSpec in typeSpecs) addType(typeSpec)
+ } as T
+ }
+}
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeVariableName.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeVariableName.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/TypeVariableName.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/TypeVariableName.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Util.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Util.kt
similarity index 99%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/Util.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Util.kt
index 16a443d..c226f39 100644
--- a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/Util.kt
+++ b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/Util.kt
@@ -169,7 +169,7 @@
internal val String.isIdentifier get() = IDENTIFIER_REGEX.matches(this)
// https://kotlinlang.org/docs/reference/keyword-reference.html
-private val KEYWORDS = setOf(
+internal val KEYWORDS = setOf(
// Hard keywords
"as",
"break",
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/WildcardTypeName.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/WildcardTypeName.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/WildcardTypeName.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/WildcardTypeName.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/jvm/JvmAnnotations.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/jvm/JvmAnnotations.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/jvm/JvmAnnotations.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/jvm/JvmAnnotations.kt
diff --git a/kotlinpoet/src/main/java/com/squareup/kotlinpoet/tags/TypeAliasTag.kt b/kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/tags/TypeAliasTag.kt
similarity index 100%
rename from kotlinpoet/src/main/java/com/squareup/kotlinpoet/tags/TypeAliasTag.kt
rename to kotlinpoet/src/commonMain/kotlin/com/squareup/kotlinpoet/tags/TypeAliasTag.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/AbstractTypesTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AbstractTypesTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/AbstractTypesTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AbstractTypesTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/AnnotatedTypeNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AnnotatedTypeNameTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/AnnotatedTypeNameTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AnnotatedTypeNameTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/AnnotationSpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AnnotationSpecTest.kt
similarity index 91%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/AnnotationSpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AnnotationSpecTest.kt
index fff366f..b35e65f 100644
--- a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/AnnotationSpecTest.kt
+++ b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AnnotationSpecTest.kt
@@ -345,32 +345,6 @@
)
}
- @Test fun getOnValueArrayTypeMirrorShouldNameValueArg() {
- val myClazz = compilation.elements
- .getTypeElement(JavaClassWithArrayValueAnnotation::class.java.canonicalName)
- val classBuilder = TypeSpec.classBuilder("Result")
-
- myClazz.annotationMirrors.map { AnnotationSpec.get(it) }
- .forEach {
- classBuilder.addAnnotation(it)
- }
-
- assertThat(toString(classBuilder.build())).isEqualTo(
- """
- |package com.squareup.tacos
- |
- |import com.squareup.kotlinpoet.JavaClassWithArrayValueAnnotation
- |import java.lang.Boolean
- |import java.lang.Object
- |
- |@JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue(value = arrayOf(Object::class,
- | Boolean::class))
- |public class Result
- |
- """.trimMargin(),
- )
- }
-
@Test fun getOnVarargMirrorShouldNameValueArg() {
val myClazz = compilation.elements
.getTypeElement(KotlinClassWithVarargAnnotation::class.java.canonicalName)
@@ -399,28 +373,6 @@
)
}
- @Test fun getOnValueArrayTypeAnnotationShouldNameValueArg() {
- val annotation = JavaClassWithArrayValueAnnotation::class.java.getAnnotation(
- JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue::class.java,
- )
- val classBuilder = TypeSpec.classBuilder("Result")
- .addAnnotation(AnnotationSpec.get(annotation))
-
- assertThat(toString(classBuilder.build()).trim()).isEqualTo(
- """
- |package com.squareup.tacos
- |
- |import com.squareup.kotlinpoet.JavaClassWithArrayValueAnnotation
- |import java.lang.Boolean
- |import java.lang.Object
- |
- |@JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue(value = arrayOf(Object::class,
- | Boolean::class))
- |public class Result
- """.trimMargin(),
- )
- }
-
@Test fun getOnVarargAnnotationShouldNameValueArg() {
val annotation = KotlinClassWithVarargAnnotation::class.java
.getAnnotation(AnnotationWithArrayValue::class.java)
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/AssertThrows.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AssertThrows.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/AssertThrows.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/AssertThrows.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/Cased/Weird/Sup.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/Cased/Weird/Sup.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/Cased/Weird/Sup.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/Cased/Weird/Sup.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/ClassNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ClassNameTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/ClassNameTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ClassNameTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/CodeBlockTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/CodeBlockTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/CodeBlockTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/CodeBlockTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/CrossplatformTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/CrossplatformTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/CrossplatformTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/CrossplatformTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/DelegatedConstructorCallTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/DelegatedConstructorCallTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/DelegatedConstructorCallTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/DelegatedConstructorCallTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/ExpectDeclarationsTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ExpectDeclarationsTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/ExpectDeclarationsTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ExpectDeclarationsTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/ExternalDeclarationsTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ExternalDeclarationsTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/ExternalDeclarationsTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ExternalDeclarationsTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileReadingTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileReadingTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileReadingTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileReadingTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileSpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileSpecTest.kt
similarity index 97%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileSpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileSpecTest.kt
index c363f59..a855cdd 100644
--- a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileSpecTest.kt
+++ b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileSpecTest.kt
@@ -500,6 +500,37 @@
)
}
+ // https://github.com/square/kotlinpoet/issues/1696
+ @Test fun aliasedImportInSamePackage() {
+ val packageName = "com.mypackage"
+ val className = ClassName(packageName, "StringKey")
+ val source = FileSpec.builder(packageName, "K")
+ .addAliasedImport(className, "S")
+ .addType(
+ TypeSpec
+ .objectBuilder("K")
+ .addProperty(
+ PropertySpec.builder("test", className)
+ .initializer("%T(%L)", className, 0)
+ .build(),
+ )
+ .build(),
+ )
+ .build()
+ assertThat(source.toString()).isEqualTo(
+ """
+ |package com.mypackage
+ |
+ |import com.mypackage.StringKey as S
+ |
+ |public object K {
+ | public val test: S = S(0)
+ |}
+ |
+ """.trimMargin(),
+ )
+ }
+
@Test fun conflictingParentName() {
val source = FileSpec.builder("com.squareup.tacos", "A")
.addType(
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileWritingTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileWritingTest.kt
similarity index 84%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileWritingTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileWritingTest.kt
index 83574f3..0e79ac7 100644
--- a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/FileWritingTest.kt
+++ b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FileWritingTest.kt
@@ -69,37 +69,46 @@
@Test fun pathDefaultPackage() {
val type = TypeSpec.classBuilder("Test").build()
- FileSpec.get("", type).writeTo(fsRoot)
+ val fileSpec = FileSpec.get("", type)
+ assertThat(fileSpec.relativePath).isEqualTo("Test.kt")
- val testPath = fsRoot.resolve("Test.kt")
+ val testPath = fileSpec.writeTo(fsRoot)
+ assertThat(testPath).isEqualTo(fsRoot.resolve("Test.kt"))
assertThat(Files.exists(testPath)).isTrue()
}
@Test fun pathDefaultPackageWithSubdirectory() {
val type = TypeSpec.classBuilder("Test").build()
- FileSpec.get("", type).writeTo(fsRoot.resolve("sub"))
+ val testPath = FileSpec.get("", type).writeTo(fsRoot.resolve("sub"))
- val testPath = fsRoot.resolve("sub/Test.kt")
+ assertThat(testPath).isEqualTo(fsRoot.resolve("sub/Test.kt"))
assertThat(Files.exists(testPath)).isTrue()
}
@Test fun fileDefaultPackage() {
val type = TypeSpec.classBuilder("Test").build()
- FileSpec.get("", type).writeTo(tmp.root)
+ val testFile = FileSpec.get("", type).writeTo(tmp.root)
- val testFile = File(tmp.root, "Test.kt")
+ assertThat(testFile).isEqualTo(File(tmp.root, "Test.kt"))
assertThat(testFile.exists()).isTrue()
}
@Test fun pathNestedClasses() {
val type = TypeSpec.classBuilder("Test").build()
- FileSpec.get("foo", type).writeTo(fsRoot)
- FileSpec.get("foo.bar", type).writeTo(fsRoot)
- FileSpec.get("foo.bar.baz", type).writeTo(fsRoot)
+ val fooSpec = FileSpec.get("foo", type)
+ val barSpec = FileSpec.get("foo.bar", type)
+ val bazSpec = FileSpec.get("foo.bar.baz", type)
+ assertThat(fooSpec.relativePath).isEqualTo("foo/Test.kt")
+ assertThat(barSpec.relativePath).isEqualTo("foo/bar/Test.kt")
+ assertThat(bazSpec.relativePath).isEqualTo("foo/bar/baz/Test.kt")
- val fooPath = fsRoot.resolve(fs.getPath("foo", "Test.kt"))
- val barPath = fsRoot.resolve(fs.getPath("foo", "bar", "Test.kt"))
- val bazPath = fsRoot.resolve(fs.getPath("foo", "bar", "baz", "Test.kt"))
+ val fooPath = fooSpec.writeTo(fsRoot)
+ val barPath = barSpec.writeTo(fsRoot)
+ val bazPath = bazSpec.writeTo(fsRoot)
+
+ assertThat(fooPath).isEqualTo(fsRoot.resolve(fs.getPath("foo", "Test.kt")))
+ assertThat(barPath).isEqualTo(fsRoot.resolve(fs.getPath("foo", "bar", "Test.kt")))
+ assertThat(bazPath).isEqualTo(fsRoot.resolve(fs.getPath("foo", "bar", "baz", "Test.kt")))
assertThat(Files.exists(fooPath)).isTrue()
assertThat(Files.exists(barPath)).isTrue()
assertThat(Files.exists(bazPath)).isTrue()
@@ -107,16 +116,16 @@
@Test fun fileNestedClasses() {
val type = TypeSpec.classBuilder("Test").build()
- FileSpec.get("foo", type).writeTo(tmp.root)
- FileSpec.get("foo.bar", type).writeTo(tmp.root)
- FileSpec.get("foo.bar.baz", type).writeTo(tmp.root)
+ val fooFile = FileSpec.get("foo", type).writeTo(tmp.root)
+ val barFile = FileSpec.get("foo.bar", type).writeTo(tmp.root)
+ val bazFile = FileSpec.get("foo.bar.baz", type).writeTo(tmp.root)
val fooDir = File(tmp.root, "foo")
- val fooFile = File(fooDir, "Test.kt")
+ assertThat(fooFile).isEqualTo(File(fooDir, "Test.kt"))
val barDir = File(fooDir, "bar")
- val barFile = File(barDir, "Test.kt")
+ assertThat(barFile).isEqualTo(File(barDir, "Test.kt"))
val bazDir = File(barDir, "baz")
- val bazFile = File(bazDir, "Test.kt")
+ assertThat(bazFile).isEqualTo(File(bazDir, "Test.kt"))
assertThat(fooFile.exists()).isTrue()
assertThat(barFile.exists()).isTrue()
assertThat(bazFile.exists()).isTrue()
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/FunSpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FunSpecTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/FunSpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/FunSpecTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/KotlinPoetTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/KotlinPoetTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/KotlinPoetTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/KotlinPoetTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/LambdaTypeNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/LambdaTypeNameTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/LambdaTypeNameTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/LambdaTypeNameTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/LineWrapperTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/LineWrapperTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/LineWrapperTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/LineWrapperTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/LineWrappingTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/LineWrappingTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/LineWrappingTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/LineWrappingTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/MemberNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/MemberNameTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/MemberNameTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/NameAllocatorTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/NameAllocatorTest.kt
similarity index 93%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/NameAllocatorTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/NameAllocatorTest.kt
index 28c7d34..a61877c 100644
--- a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/NameAllocatorTest.kt
+++ b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/NameAllocatorTest.kt
@@ -70,6 +70,12 @@
assertThat(nameAllocator[1]).isEqualTo("when_")
}
+ @Test fun kotlinKeywordNotPreAllocated() {
+ val nameAllocator = NameAllocator(preallocateKeywords = false)
+ assertThat(nameAllocator.newName("when", 1)).isEqualTo("when")
+ assertThat(nameAllocator[1]).isEqualTo("when")
+ }
+
@Test fun tagReuseForbidden() {
val nameAllocator = NameAllocator()
nameAllocator.newName("foo", 1)
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/ParameterSpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ParameterSpecTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/ParameterSpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ParameterSpecTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/ParameterizedTypeNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ParameterizedTypeNameTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/ParameterizedTypeNameTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ParameterizedTypeNameTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/PropertySpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/PropertySpecTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/PropertySpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/PropertySpecTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/StringsTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/StringsTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/StringsTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/StringsTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TaggableTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TaggableTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TaggableTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TaggableTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TestFiler.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TestFiler.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TestFiler.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TestFiler.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeAliasSpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeAliasSpecTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeAliasSpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeAliasSpecTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeNameKotlinTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeNameKotlinTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeNameKotlinTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeNameKotlinTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeSpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeSpecTest.kt
similarity index 99%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeSpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeSpecTest.kt
index ebac635..94732b6 100644
--- a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeSpecTest.kt
+++ b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeSpecTest.kt
@@ -1975,14 +1975,12 @@
| * [random][java.util.Random] tex-mex stuff we could find in the pantry
| * and some [kotlin.String] cheese.
| *
+ | * @param temperature Taco temperature. Can be as cold as the famous ice tacos from
+ | * the Andes, or hot with lava-like cheese from the depths of
+ | * the Ninth Circle.
| * @param mild Whether the taco is mild (ew) or crunchy (ye).
| */
|public class Taco(
- | /**
- | * Taco temperature. Can be as cold as the famous ice tacos from
- | * the Andes, or hot with lava-like cheese from the depths of
- | * the Ninth Circle.
- | */
| temperature: Double,
| /**
| * True for a soft flour tortilla; false for a crunchy corn tortilla.
@@ -4805,6 +4803,9 @@
| * This is a thing for stuff.
| *
| * @constructor Construct a thing!
+ | * @param first the first thing
+ | * @param second the second thing
+ | * @param third the third thing
| */
|public class MyType(
| /**
@@ -4839,10 +4840,10 @@
.build()
assertThat(typeSpec.toString()).isEqualTo(
"""
+ |/**
+ | * @param first the first thing
+ | */
|public class MyType(
- | /**
- | * the first thing
- | */
| first: kotlin.Int,
|)
|
@@ -5615,6 +5616,27 @@
)
}
+ @Test fun classKdoc() {
+ val type = TypeSpec.classBuilder("MyClass")
+ .addKdoc("This is my class")
+ .primaryConstructor(
+ FunSpec.constructorBuilder()
+ .build(),
+ )
+ .build()
+
+ //language=kotlin
+ assertThat(type.toString()).isEqualTo(
+ """
+ /**
+ * This is my class
+ */
+ public class MyClass()
+
+ """.trimIndent(),
+ )
+ }
+
// https://github.com/square/kotlinpoet/issues/1630
@Test fun primaryConstructorKDoc() {
val type = TypeSpec.classBuilder("MyClass")
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeVariableNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeVariableNameTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeVariableNameTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypeVariableNameTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypesEclipseTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypesEclipseTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypesEclipseTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypesEclipseTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypesTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypesTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypesTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/TypesTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/UtilTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/UtilTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/UtilTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/UtilTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/ValueTypeSpecTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ValueTypeSpecTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/ValueTypeSpecTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/ValueTypeSpecTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/WildcardTypeNameTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/WildcardTypeNameTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/WildcardTypeNameTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/WildcardTypeNameTest.kt
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/jvm/JvmAnnotationsTest.kt b/kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/jvm/JvmAnnotationsTest.kt
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/jvm/JvmAnnotationsTest.kt
rename to kotlinpoet/src/commonTest/kotlin/com/squareup/kotlinpoet/jvm/JvmAnnotationsTest.kt
diff --git a/kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/JavaAnnotationSpecTest.kt b/kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/JavaAnnotationSpecTest.kt
new file mode 100644
index 0000000..4b26596
--- /dev/null
+++ b/kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/JavaAnnotationSpecTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.squareup.kotlinpoet
+
+import com.google.common.truth.Truth.assertThat
+import com.google.testing.compile.CompilationRule
+import kotlin.test.Test
+import org.junit.Rule
+
+class JavaAnnotationSpecTest {
+
+ @Rule @JvmField
+ val compilation = CompilationRule()
+
+ @Test fun getOnValueArrayTypeMirrorShouldNameValueArg() {
+ val myClazz = compilation.elements
+ .getTypeElement(JavaClassWithArrayValueAnnotation::class.java.canonicalName)
+ val classBuilder = TypeSpec.classBuilder("Result")
+
+ myClazz.annotationMirrors.map { AnnotationSpec.get(it) }
+ .forEach {
+ classBuilder.addAnnotation(it)
+ }
+
+ assertThat(toString(classBuilder.build())).isEqualTo(
+ """
+ |package com.squareup.tacos
+ |
+ |import com.squareup.kotlinpoet.JavaClassWithArrayValueAnnotation
+ |import java.lang.Boolean
+ |import java.lang.Object
+ |
+ |@JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue(value = arrayOf(Object::class,
+ | Boolean::class))
+ |public class Result
+ |
+ """.trimMargin(),
+ )
+ }
+
+ @Test fun getOnValueArrayTypeAnnotationShouldNameValueArg() {
+ val annotation = JavaClassWithArrayValueAnnotation::class.java.getAnnotation(
+ JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue::class.java,
+ )
+ val classBuilder = TypeSpec.classBuilder("Result")
+ .addAnnotation(AnnotationSpec.get(annotation))
+
+ assertThat(toString(classBuilder.build()).trim()).isEqualTo(
+ """
+ |package com.squareup.tacos
+ |
+ |import com.squareup.kotlinpoet.JavaClassWithArrayValueAnnotation
+ |import java.lang.Boolean
+ |import java.lang.Object
+ |
+ |@JavaClassWithArrayValueAnnotation.AnnotationWithArrayValue(value = arrayOf(Object::class,
+ | Boolean::class))
+ |public class Result
+ """.trimMargin(),
+ )
+ }
+
+ private fun toString(typeSpec: TypeSpec) =
+ FileSpec.get("com.squareup.tacos", typeSpec).toString()
+}
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/JavaClassWithArrayValueAnnotation.java b/kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/JavaClassWithArrayValueAnnotation.java
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/JavaClassWithArrayValueAnnotation.java
rename to kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/JavaClassWithArrayValueAnnotation.java
diff --git a/kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeNameTest.java b/kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/TypeNameTest.java
similarity index 100%
rename from kotlinpoet/src/test/java/com/squareup/kotlinpoet/TypeNameTest.java
rename to kotlinpoet/src/jvmTest/java/com/squareup/kotlinpoet/TypeNameTest.java
diff --git a/mkdocs.yml b/mkdocs.yml
index 74caf79..abd937b 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -55,10 +55,30 @@
nav:
- 'Overview':
- - 'KotlinPoet': index.md
- - 'Interop - JavaPoet': interop-javapoet.md
- - 'Interop - kotlinx-metadata': interop-kotlinx-metadata.md
- - 'Interop - KSP': interop-ksp.md
+ - 'Code & Control Flow': code-control-flow.md
+ - 'Format Specifiers':
+ - '%S for Strings': s-for-strings.md
+ - '%P for String Templates': p-for-string-templates.md
+ - '%T for Types': t-for-types.md
+ - '%M for Members': m-for-members.md
+ - '%N for Names': n-for-names.md
+ - '%L for Literals': l-for-literals.md
+ - 'Code Block Format Strings': code-block-format-strings.md
+ - 'Functions': functions.md
+ - 'Constructors': constructors.md
+ - 'Parameters': parameters.md
+ - 'Properties': properties.md
+ - 'Interfaces': interfaces.md
+ - 'Objects': objects.md
+ - 'Enums': enums.md
+ - 'Anonymous Inner Classes': anonymous-inner-classes.md
+ - 'Annotations': annotations.md
+ - 'Type Aliases': type-aliases.md
+ - 'Callable References': callable-references.md
+ - 'kotlin-reflect': kotlin-reflect.md
+ - 'Interop - JavaPoet': interop-javapoet.md
+ - 'Interop - kotlinx-metadata': interop-kotlinx-metadata.md
+ - 'Interop - KSP': interop-ksp.md
- 'API':
- 'kotlinpoet': 1.x/kotlinpoet/index.html
- 'interop-javapoet': 1.x/interop-javapoet/index.html
diff --git a/renovate.json b/renovate.json
index 47fc816..d77f3a6 100644
--- a/renovate.json
+++ b/renovate.json
@@ -3,6 +3,7 @@
"extends": [
"config:base"
],
+ "semanticCommits": "disabled",
"packageRules": [
{
"matchManagers": ["pip_requirements"],
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 465f238..c9b262e 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -21,7 +21,7 @@
}
plugins {
- id("org.gradle.toolchains.foojay-resolver-convention") version("0.7.0")
+ id("org.gradle.toolchains.foojay-resolver-convention") version("0.8.0")
}
include(