From ea54924ae711bf8ad5160754c2cff3c8741d12bd Mon Sep 17 00:00:00 2001 From: Alexander Perfilyev Date: Tue, 18 Apr 2023 22:42:07 +0300 Subject: [PATCH 1/3] Create view intention and live template --- .../intentions/CreateViewIntention.kt | 74 ++++++++++++++ .../src/main/resources/META-INF/plugin.xml | 4 + .../CreateViewIntention/description.html | 5 + .../resources/liveTemplates/SqlDelight.xml | 8 ++ .../intentions/CreateViewIntentionTest.kt | 98 +++++++++++++++++++ 5 files changed, 189 insertions(+) create mode 100644 sqldelight-idea-plugin/src/main/kotlin/app/cash/sqldelight/intellij/intentions/CreateViewIntention.kt create mode 100644 sqldelight-idea-plugin/src/main/resources/intentionDescriptions/CreateViewIntention/description.html create mode 100644 sqldelight-idea-plugin/src/test/kotlin/app/cash/sqldelight/intellij/intentions/CreateViewIntentionTest.kt diff --git a/sqldelight-idea-plugin/src/main/kotlin/app/cash/sqldelight/intellij/intentions/CreateViewIntention.kt b/sqldelight-idea-plugin/src/main/kotlin/app/cash/sqldelight/intellij/intentions/CreateViewIntention.kt new file mode 100644 index 00000000000..e488caeefc8 --- /dev/null +++ b/sqldelight-idea-plugin/src/main/kotlin/app/cash/sqldelight/intellij/intentions/CreateViewIntention.kt @@ -0,0 +1,74 @@ +package app.cash.sqldelight.intellij.intentions + +import com.alecstrong.sql.psi.core.psi.SqlCompoundSelectStmt +import com.alecstrong.sql.psi.core.psi.SqlCreateViewStmt +import com.alecstrong.sql.psi.core.psi.SqlStmtList +import com.alecstrong.sql.psi.core.psi.SqlTypes +import com.intellij.codeInsight.intention.BaseElementAtCaretIntentionAction +import com.intellij.codeInsight.template.TemplateManager +import com.intellij.codeInsight.template.impl.TextExpression +import com.intellij.openapi.command.WriteCommandAction +import com.intellij.openapi.editor.Document +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.parentOfType +import org.jetbrains.kotlin.psi.psiUtil.endOffset +import org.jetbrains.kotlin.psi.psiUtil.startOffset + +internal class CreateViewIntention : BaseElementAtCaretIntentionAction() { + + override fun getFamilyName(): String { + return INTENTIONS_FAMILY_NAME_REFACTORINGS + } + + override fun getText(): String { + return "Create view" + } + + override fun isAvailable(project: Project, editor: Editor?, element: PsiElement): Boolean { + val selectStmt = element.parentOfType(true) + return selectStmt != null && selectStmt.parentOfType() == null + } + + override fun invoke(project: Project, editor: Editor, element: PsiElement) { + PsiDocumentManager.getInstance(project).commitAllDocuments() + + val selectStmt = element.parentOfType(true) ?: return + val stmtList = selectStmt.parentOfType() ?: return + val container = stmtList.stmtList.firstOrNull { PsiTreeUtil.isAncestor(it, selectStmt, true) } ?: return + + val semi = PsiTreeUtil.findSiblingForward(container, SqlTypes.SEMI, false, null) ?: return + + val containerStart = container.startOffset + val containerEnd = semi.endOffset + val text = editor.document.getDocumentTextFragment(containerStart, containerEnd) + val offset = selectStmt.startOffset - containerStart + + val templateManager = TemplateManager.getInstance(project) + + WriteCommandAction.runWriteCommandAction(project) { + editor.document.deleteString(containerStart, containerEnd) + + val template = templateManager.createTemplate("", "") + template.addTextSegment(text.substring(0, offset)) + template.addTextSegment("SELECT * FROM ") + val expression = TextExpression("some_view") + template.addVariableSegment("NAME") + template.addSelectionStartVariable() + template.addTextSegment(text.substring(selectStmt.endOffset - containerStart)) + + template.addTextSegment("\n\nCREATE VIEW ") + template.addVariable("NAME", expression, true) + template.addTextSegment(" AS ${selectStmt.text};") + + templateManager.startTemplate(editor, template) + } + } + + private fun Document.getDocumentTextFragment(startOffset: Int, endOffset: Int): String { + return charsSequence.subSequence(startOffset, endOffset).toString() + } +} 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 c372f045374..c7726d110e6 100644 --- a/sqldelight-idea-plugin/src/main/resources/META-INF/plugin.xml +++ b/sqldelight-idea-plugin/src/main/resources/META-INF/plugin.xml @@ -136,6 +136,10 @@ app.cash.sqldelight.intellij.intentions.QualifyColumnNameIntention SQLDelight + + app.cash.sqldelight.intellij.intentions.CreateViewIntention + SQLDelight + diff --git a/sqldelight-idea-plugin/src/main/resources/intentionDescriptions/CreateViewIntention/description.html b/sqldelight-idea-plugin/src/main/resources/intentionDescriptions/CreateViewIntention/description.html new file mode 100644 index 00000000000..2409a734275 --- /dev/null +++ b/sqldelight-idea-plugin/src/main/resources/intentionDescriptions/CreateViewIntention/description.html @@ -0,0 +1,5 @@ + + +Creates a VIEW from SELECT statement + + \ No newline at end of file diff --git a/sqldelight-idea-plugin/src/main/resources/liveTemplates/SqlDelight.xml b/sqldelight-idea-plugin/src/main/resources/liveTemplates/SqlDelight.xml index 066310c3965..6f257b122a1 100644 --- a/sqldelight-idea-plugin/src/main/resources/liveTemplates/SqlDelight.xml +++ b/sqldelight-idea-plugin/src/main/resources/liveTemplates/SqlDelight.xml @@ -68,4 +68,12 @@