diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index df5f4562c53..7714cf2ddcc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -76,7 +76,7 @@ kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin-js = { id = "org.jetbrains.kotlin.js", version.ref = "kotlin" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } -intellij = { id = "org.jetbrains.intellij", version = "1.5.3" } +intellij = { id = "org.jetbrains.intellij", version = "1.5.2" } grammarKitComposer = { id = "com.alecstrong.grammar.kit.composer", version = "0.1.9" } publish = { id = "com.vanniktech.maven.publish", version = "0.19.0" } spotless = { id = "com.diffplug.spotless", version = "6.4.2" } # When updating, check if we can remove base plugin from root build.gradle diff --git a/sqldelight-idea-plugin/build.gradle b/sqldelight-idea-plugin/build.gradle index 8277b892213..71f20536695 100644 --- a/sqldelight-idea-plugin/build.gradle +++ b/sqldelight-idea-plugin/build.gradle @@ -18,6 +18,7 @@ intellij { "org.jetbrains.kotlin", "com.intellij.gradle", "com.intellij.java", + "org.jetbrains.android" ] if (!project.version.endsWith("SNAPSHOT")) { patchPluginXml { diff --git a/sqldelight-idea-plugin/src/main/kotlin/app/cash/sqldelight/intellij/run/android/SqlDelightRunAnnotator.kt b/sqldelight-idea-plugin/src/main/kotlin/app/cash/sqldelight/intellij/run/android/SqlDelightRunAnnotator.kt new file mode 100644 index 00000000000..c36bd4c287f --- /dev/null +++ b/sqldelight-idea-plugin/src/main/kotlin/app/cash/sqldelight/intellij/run/android/SqlDelightRunAnnotator.kt @@ -0,0 +1,93 @@ +package app.cash.sqldelight.intellij.run.android + +import app.cash.sqldelight.core.lang.util.rawSqlText +import com.alecstrong.sql.psi.core.psi.SqlStmt +import com.alecstrong.sql.psi.core.psi.SqlStmtList +import com.android.tools.idea.sqlite.DatabaseInspectorProjectService +import com.android.tools.idea.sqlite.annotator.RunSqliteStatementGutterIconAction +import com.android.tools.idea.sqlite.ui.DatabaseInspectorViewsFactoryImpl +import com.intellij.lang.annotation.AnnotationHolder +import com.intellij.lang.annotation.Annotator +import com.intellij.lang.annotation.HighlightSeverity +import com.intellij.lang.java.JavaLanguage +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.editor.markup.GutterIconRenderer +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFileFactory +import com.intellij.psi.PsiLanguageInjectionHost +import com.intellij.psi.SmartPointerManager +import com.intellij.psi.SmartPsiElementPointer +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.util.ui.EmptyIcon +import icons.StudioIcons +import javax.swing.Icon + +class SqlDelightRunAnnotator : Annotator { + override fun annotate( + element: PsiElement, + holder: AnnotationHolder + ) { + if (element.parent !is SqlStmtList || element !is SqlStmt) return + + if (!PsiTreeUtil.hasErrorElements(element.containingFile)) { + val smartPsiElementPointer = SmartPointerManager.createPointer(element) + holder.newAnnotation(HighlightSeverity.INFORMATION, "") + .gutterIconRenderer(RunSqliteStatementGutterIconRenderer(smartPsiElementPointer)) + .create() + } + } + + /** + * Shows an icon in the gutter when a SQLite statement is recognized. eg. Room @Query annotations. + */ + private data class RunSqliteStatementGutterIconRenderer( + private val element: SmartPsiElementPointer + ) : GutterIconRenderer() { + private val sqliteExplorerProjectService = + DatabaseInspectorProjectService.getInstance(element.project) + + override fun getIcon(): Icon { + return if (sqliteExplorerProjectService.hasOpenDatabase()) { + StudioIcons.DatabaseInspector.NEW_QUERY + } else { + EmptyIcon.ICON_0 + } + } + + override fun getTooltipText() = "Run Sqlite statement in Database Inspector" + override fun isNavigateAction() = sqliteExplorerProjectService.hasOpenDatabase() + override fun getClickAction() = SqlDelightRunStatementAction(element) + } + + private class SqlDelightRunStatementAction( + private val originalElement: SmartPsiElementPointer + ) : AnAction() { + override fun actionPerformed(e: AnActionEvent) { + val element = originalElement.element ?: return + + val text = element.rawSqlText().trim().replace("\\s+".toRegex(), " ") + val s = """ + |package com.example; + | + |import android.database.sqlite.SQLiteDatabase; + | + |class Util { + | void f(SQLiteDatabase db) { + | db.execSQL("$text"); + | } + |} + """.trimMargin() + + val psiFile = PsiFileFactory.getInstance(element.project) + .createFileFromText("Util.java", JavaLanguage.INSTANCE, s) + + val host = + PsiTreeUtil.findChildOfType(psiFile, PsiLanguageInjectionHost::class.java) ?: return + + RunSqliteStatementGutterIconAction( + host.project, host, DatabaseInspectorViewsFactoryImpl.getInstance() + ).actionPerformed(e) + } + } +} diff --git a/sqldelight-idea-plugin/src/main/resources/META-INF/com.squareup.sqldelight-withAndroid.xml b/sqldelight-idea-plugin/src/main/resources/META-INF/com.squareup.sqldelight-withAndroid.xml new file mode 100644 index 00000000000..82204da30c5 --- /dev/null +++ b/sqldelight-idea-plugin/src/main/resources/META-INF/com.squareup.sqldelight-withAndroid.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/sqldelight-idea-plugin/src/main/resources/META-INF/plugin.xml b/sqldelight-idea-plugin/src/main/resources/META-INF/plugin.xml index 23efa8d72d4..9629a01b8f2 100644 --- a/sqldelight-idea-plugin/src/main/resources/META-INF/plugin.xml +++ b/sqldelight-idea-plugin/src/main/resources/META-INF/plugin.xml @@ -8,6 +8,7 @@ com.intellij.java org.jetbrains.kotlin com.intellij.gradle + org.jetbrains.android