Skip to content

Commit

Permalink
Support javadoc on create table statements (#1802)
Browse files Browse the repository at this point in the history
  • Loading branch information
Alec Strong committed Jun 20, 2020
1 parent 333ae50 commit a4382c0
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 37 deletions.
Expand Up @@ -8,6 +8,7 @@ import com.intellij.psi.PsiWhiteSpace
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.NameAllocator
import com.squareup.sqldelight.core.compiler.integration.javadocText
import com.squareup.sqldelight.core.compiler.model.BindableQuery
import com.squareup.sqldelight.core.compiler.model.NamedQuery
import com.squareup.sqldelight.core.lang.DRIVER_NAME
Expand Down Expand Up @@ -176,6 +177,6 @@ abstract class QueryGenerator(private val query: BindableQuery) {
}

protected fun addJavadoc(builder: FunSpec.Builder) {
query.javadocText()?.let { builder.addKdoc(it) }
if (query.javadoc != null) javadocText(query.javadoc)?.let { builder.addKdoc(it) }
}
}
Expand Up @@ -16,6 +16,9 @@
package com.squareup.sqldelight.core.compiler

import com.alecstrong.sql.psi.core.psi.LazyQuery
import com.alecstrong.sql.psi.core.psi.SqlStmt
import com.alecstrong.sql.psi.core.psi.SqlTypes
import com.intellij.psi.util.PsiTreeUtil
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier.DATA
Expand All @@ -27,10 +30,13 @@ import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.asClassName
import com.squareup.kotlinpoet.joinToCode
import com.squareup.sqldelight.core.compiler.SqlDelightCompiler.allocateName
import com.squareup.sqldelight.core.compiler.integration.javadocText
import com.squareup.sqldelight.core.lang.ADAPTER_NAME
import com.squareup.sqldelight.core.lang.psi.ColumnDefMixin
import com.squareup.sqldelight.core.lang.psi.ColumnDefMixin.Companion.isArrayType
import com.squareup.sqldelight.core.lang.util.childOfType
import com.squareup.sqldelight.core.lang.util.parentOfType
import com.squareup.sqldelight.core.psi.SqlDelightStmtIdentifier

internal class TableInterfaceGenerator(private val table: LazyQuery) {
private val typeName = allocateName(table.tableName).capitalize()
Expand All @@ -39,6 +45,14 @@ internal class TableInterfaceGenerator(private val table: LazyQuery) {
val typeSpec = TypeSpec.classBuilder(typeName)
.addModifiers(DATA)

val identifier = PsiTreeUtil.getPrevSiblingOfType(
PsiTreeUtil.getParentOfType(table.tableName, SqlStmt::class.java),
SqlDelightStmtIdentifier::class.java
)
identifier?.childOfType(SqlTypes.JAVADOC)?.let { javadoc ->
javadocText(javadoc)?.let { typeSpec.addKdoc(it) }
}

val propertyPrints = mutableListOf<CodeBlock>()
val contentToString = MemberName("kotlin.collections", "contentToString")

Expand Down
@@ -0,0 +1,37 @@
package com.squareup.sqldelight.core.compiler.integration

import com.intellij.psi.PsiElement

/**
* This pattern consists of 3 parts:
*
* - `/\\*\\*` - matches the first line of the Javadoc:
*
* ```
* </**>
* * Javadoc
* */
* ```
*
* - `\n \\*[ /]?` - matches every other line of Javadoc:
*
* ```
* /**<
* * >Javadoc<
* */>
* ```
*
* - ` \\**slash` - specifically matches the tail part of a single-line Javadoc:
*
* ```
* /* Javadoc< */>
* ```
*/
private val JAVADOC_TEXT_REGEX = Regex("/\\*\\*|\n \\*[ /]?| \\*/")

internal fun javadocText(javadoc: PsiElement): String? {
return javadoc.text
.split(JAVADOC_TEXT_REGEX)
.dropWhile(String::isEmpty)
.joinToString(separator = "\n", transform = String::trim)
}
Expand Up @@ -41,7 +41,7 @@ abstract class BindableQuery(
) {
abstract val id: Int

private val javadoc: PsiElement? = identifier?.childOfType(SqlTypes.JAVADOC)
internal val javadoc: PsiElement? = identifier?.childOfType(SqlTypes.JAVADOC)

/**
* The collection of parameters exposed in the generated api for this query.
Expand Down Expand Up @@ -167,48 +167,13 @@ abstract class BindableQuery(
private val SqlBindParameter.identifier: SqlIdentifier?
get() = childOfType(SqlTypes.IDENTIFIER) as? SqlIdentifier

internal fun javadocText(): String? {
if (javadoc == null) return null
return javadoc.text
.split(JAVADOC_TEXT_REGEX)
.dropWhile(String::isEmpty)
.joinToString(separator = "\n", transform = String::trim)
}

internal data class Argument(
val index: Int,
val type: IntermediateType,
val bindArgs: MutableList<SqlBindExpr> = mutableListOf()
)

companion object {
/**
* This pattern consists of 3 parts:
*
* - `/\\*\\*` - matches the first line of the Javadoc:
*
* ```
* </**>
* * Javadoc
* */
* ```
*
* - `\n \\*[ /]?` - matches every other line of Javadoc:
*
* ```
* /**<
* * >Javadoc<
* */>
* ```
*
* - ` \\**slash` - specifically matches the tail part of a single-line Javadoc:
*
* ```
* /* Javadoc< */>
* ```
*/
private val JAVADOC_TEXT_REGEX = Regex("/\\*\\*|\n \\*[ /]?| \\*/")

/**
* The query id map use to avoid string hashcode collision. Ideally this map should be per module.
*/
Expand Down
Expand Up @@ -30,6 +30,32 @@ class JavadocTest {
|""".trimMargin())
}

@Test fun `select - properly formatted javadoc when there are two`() {
val file = FixtureCompiler.parseSql(CREATE_TABLE + """
|/**
| * Queries all values.
| */
|selectAll:
|SELECT *
|FROM test;
|
|/**
| * Queries all values.
| */
|selectAll2:
|SELECT *
|FROM test;
|""".trimMargin(), tempFolder)

val selectGenerator = SelectQueryGenerator(file.namedQueries.first())
assertThat(selectGenerator.defaultResultTypeFunction().toString()).isEqualTo("""
|/**
| * Queries all values.
| */
|override fun selectAll(): com.squareup.sqldelight.Query<com.example.Test> = selectAll(::com.example.Test)
|""".trimMargin())
}

@Test fun `select - multiline javadoc`() {
val file = FixtureCompiler.parseSql(CREATE_TABLE + """
|/**
Expand Down
Expand Up @@ -29,6 +29,41 @@ class SelectQueryFunctionTest {
|""".trimMargin())
}

@Test fun `infer type for between`() {
val file = FixtureCompiler.parseSql("""
|import com.example.LocalDateTime;
|
|CREATE TABLE data (
| channelId TEXT NOT NULL,
| startTime INTEGER AS LocalDateTime NOT NULL,
| endTime INTEGER AS LocalDateTime NOT NULL
|);
|
|selectByChannelId:
|SELECT *
|FROM data
|WHERE channelId =?
|AND (
| startTime BETWEEN :from AND :to
| OR
| endTime BETWEEN :from AND :to
| OR
| :from BETWEEN startTime AND endTime
| OR
| :to BETWEEN startTime AND endTime
|);
|""".trimMargin(), tempFolder)

val generator = SelectQueryGenerator(file.namedQueries.first())
assertThat(generator.defaultResultTypeFunction().toString()).isEqualTo("""
|override fun selectByChannelId(
| channelId: kotlin.String,
| from: com.example.LocalDateTime,
| to: com.example.LocalDateTime
|): com.squareup.sqldelight.Query<com.example.Data> = selectByChannelId(channelId, from, to, ::com.example.Data)
|""".trimMargin())
}

@Test fun `query bind args appear in the correct order`() {
val file = FixtureCompiler.parseSql("""
|CREATE TABLE data (
Expand Down
Expand Up @@ -2,6 +2,9 @@ import java.util.List;
import com.sample.Person;
import com.squareup.Redacted;

/**
* This is a person
*/
CREATE TABLE person (
_id INTEGER NOT NULL PRIMARY KEY,
name TEXT NOT NULL,
Expand Down
Expand Up @@ -8,6 +8,9 @@ import kotlin.ByteArray
import kotlin.Long
import kotlin.String

/**
* This is a person
*/
data class Person(
val _id: Long,
val name: String,
Expand Down

0 comments on commit a4382c0

Please sign in to comment.