## 1. Project Overview & Quickstart (square/kotlinpoet) ## File: README.md KotlinPoet ========== `KotlinPoet` is a Kotlin and Java API for generating `.kt` source files. ### [square.github.io/kotlinpoet](https://square.github.io/kotlinpoet) License ------- Copyright 2017 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. --- ## File: docs/annotations.md 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 ``` ## Annotating Types KotlinPoet provides a convenient `annotated()` API for adding annotations to types: ```kotlin // Add a single annotation. val annotatedType = String::class.asTypeName() .annotated(AnnotationSpec.builder(MyAnnotation::class).build()) // Add multiple annotations. val multiAnnotatedType = Int::class.asTypeName() .annotated( AnnotationSpec.builder(Suppress::class).addMember("%S", "unused").build(), AnnotationSpec.builder(Deprecated::class).addMember("%S", "Use something else").build() ) // Add annotations by class. val simpleAnnotated = String::class.asTypeName().annotated(Suppress::class, Deprecated::class) // Chain multiple calls. val chainedAnnotations = String::class.asTypeName() .annotated(Suppress::class) .annotated(Deprecated::class) ``` This is especially useful when working with lambda types: ```kotlin val composableType = LambdaTypeName.get( receiver = null, parameters = listOf(ParameterSpec.unnamed(ClassName("androidx.compose.ui", "Modifier"))), returnType = UNIT, ).annotated(AnnotationSpec.builder(ClassName("androidx.compose.runtime", "Composable")).build()) ``` You can also pass annotations directly by class: ```kotlin val suppressedType = String::class.asTypeName().annotated(Suppress::class) ``` Or chain multiple annotations: ```kotlin val chainedAnnotations = String::class.asTypeName() .annotated(AnnotationSpec.builder(Suppress::class).addMember("%S", "unused").build()) .annotated(Deprecated::class) ``` You can also update, replace, or clear annotations on a type with the `copy()` function: ```kotlin val unannotatedString = String::class.asTypeName() .annotated(Suppress::class) .copy(annotations = listOf()) ``` --- ## File: docs/anonymous-inner-classes.md 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) { strings.sortedWith(object : Comparator { 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. --- ## File: docs/callable-references.md 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 --- ## File: docs/code-block-format-strings.md 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() map += "food" to "tacos" map += "count" to 3 CodeBlock.builder().addNamed("I ate %count:L %food:L", map) ``` --- ## File: docs/code-control-flow.md 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. --- ## File: docs/constructors.md 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. --- ## File: docs/contributing.md Contributing ============ If you would like to contribute code you can do so through GitHub by forking the repository and sending a pull request. When submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible. Please also make sure your code compiles by running `./gradlew clean build`. When creating a pull request, please add a row in the [changelog][2] with the patch description and PR # to `Unreleased` section. Before your code can be accepted into the project you must also sign the [Individual Contributor License Agreement (CLA)][1]. [1]: https://spreadsheets.google.com/spreadsheet/viewform?formkey=dDViT2xzUHAwRkI3X3k5Z0lQM091OGc6MQ&ndplr=1 [2]: https://github.com/square/kotlinpoet/blob/main/docs/changelog.md --- ## File: docs/enums.md 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"); } ``` --- ## File: docs/functions.md 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() ``` ## Wrapping is explicit In order to guarantee code correctness, starting in version 2.0, KotlinPoet will never replace spaces found in blocks of code with new line symbols, even 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() ``` The function will always be printed out like this: ```kotlin public fun foo() = (100..10000).map { number -> number * number }.map { number -> number.toString() }.also { string -> println(string) } ``` While the output is correct, the resulting line of code is quite long and hard to read. KotlinPoet is unable to understand the context of the expression and adjust the formatting for you, but there's a trick you can use to declare a breaking space - use the `♢` symbol where you know it's safe to optionally wrap the line. 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 public fun foo(): Unit = (100..10000).map { number -> number * number }.map { number -> number.toString() }.also { string -> println(string) } ``` This is slightly better, but far from perfect - feel free to play around with other formatting modifiers, such as the standard `\n` character which KotlinPoet honors, or the indentation formatters (`⇥` and `⇤`) that the library ships with (see the documentation for `CodeBlock` for more information). Lastly, imperfect formatting is a known limitation of the library, as KotlinPoet by design prioritizes correctness of generated code over style. If correct and clean formatting is important for your use case, please consider post-processing KotlinPoet's output using a dedicated code formatter. ## 2. Official Technical Reference & Guides (square/square.github.io) Square Open Source Portal ========================= [](https://travis-ci.org/square/square.github.io) A simple, static portal which outlines our open source offerings. Intentionally themed to look like a Square merchant page on the directory. Development ----------- ### Run the site locally ```bash gem install bundler # first time only bundle install # first time only bundle exec jekyll serve ``` ### Update list of repos: ```bash pip install pystache requests pygithub3 # first time only ./generate.py ``` About the code ----------- Due to the use of absolute URLs in CSS files that are (essentially) out of our control, the easiest way to develop is by running with Jekyll. Repositories are listed in the `repos.json` file as a map of repository names to a list of their categories. Invoking the `generate.py` script will update the `index.html` page with the latest repos by using the `index.mustache` file as a template. Repository data is pulled via the GitHub API (e.g., website). By default the script performs unauthenticated requests, so it's easy to run up against GitHub's limit of [60 unauthenticated requests per hour](http://developer.github.com/v3/#rate-limiting). To make authenticated requests and work around the rate-limiting, add an entry for api.github.com to your ~/.netrc file, preferably with a Personal Access Token from https://github.com/settings/tokens machine api.github.com login YourUsername password PersonalAccessToken Images are loaded by convention from the `repo_images/` directory. Ensure the name is the same as the repo name in the `repos.json` file and has a `.jpg` extension. Currently all images are rotated 10 degrees counter-clockwise to break up the overwhelming horizontal and vertical visual lines on the page. ## License ```plaintext Copyright 2021 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 http://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. ```